2017-02-22 49 views
7

Tôi có một số List<LedgerEntry> ledgerEntries và tôi cần tính số tiền của creditAmount và debitAmount.Java 8 Tổng hai thuộc tính đối tượng trong một lần lặp

class LedgerEntry{ 
private BigDecimal creditAmount; 
private BigDecimal debitAmount; 

//getters and setters 
} 

Tôi đã thực hiện điều này như,

BigDecimal creditTotal = ledgeredEntries.stream().map(p ->p.getCreditAmount()). 
reduce(BigDecimal.ZERO, BigDecimal::add); 
BigDecimal debitTotal = ledgeredEntries.stream().map(p ->p.getDebitAmount()). 
reduce(BigDecimal.ZERO, BigDecimal::add); 

//... 
//Use creditTotal, debitTotal later 

này trông giống như tôi đang iterating trên danh sách hai lần. Có cách nào để thực hiện việc này một lần mà không cần phải làm ướt danh sách hai lần không?

Pre Java 8 phiên bản

BigDecimal creditTotal = BigDecimal.ZERO; 
BigDecimal debitTotal = BigDecimal.ZERO; 
for(LedgerEntry entry : ledgerEntries){ 
    creditTotal = creditTotal.add(entry.getCreditAmount()); 
    debitTotal = debitTotal.add(entry.getDebitAmount()); 
} 
+3

Tại sao bạn muốn sử dụng dòng? Phiên bản "Pre Java 8" của bạn cũng là Java 8% hợp lệ và (khi được sửa cho thực tế là nó không thực sự làm bất cứ điều gì vì 'BigDecimal' là bất biến) dễ đọc hơn và dễ bảo trì hơn (và có thể thực hiện được nhiều hơn) giải pháp cố gắng tính hai khoản tiền cùng một lúc. – Hoopje

+0

@KrazyKalle: Cảm ơn. đã chỉnh sửa – Krishan

+0

@KrazyKalle. Vâng. Bạn nghĩ gì tôi có ý nghĩa với câu giữa các dấu ngoặc đơn (khi cố định ... bất biến)? – Hoopje

Trả lời

12

Bạn có thể làm giảm tới một mục tổng số:

LedgerEntry totalsEntry = entries.stream().reduce(new LedgerEntry(), (te, e) -> { 
    te.setCreditAmount(te.getCreditAmount().add(e.getCreditAmount())); 
    te.setDebitAmount(te.getDebitAmount().add(e.getDebitAmount())); 

    return te; 
}); 

Cập nhật

Trong những ý kiến ​​đó đã được chỉ ra một cách chính xác rằng reduce() không nên sửa đổi số nhận dạng ban đầu giá trị, và rằng collect() nên được sử dụng để giảm đột biến. Dưới đây là phiên bản sử dụng collect() (sử dụng cùng một BiConsumer làm cả bộ tích lũy và bộ kết hợp). Nó cũng giải quyết vấn đề NPE tiềm năng nếu các giá trị creditAmount và/hoặc debitAmount chưa được đặt.

BiConsumer<LedgerEntry, LedgerEntry> ac = (e1, e2) -> { 
    BigDecimal creditAmount = e1.getCreditAmount() != null ? e1.getCreditAmount() : BigDecimal.ZERO; 
    BigDecimal debitAmount = e1.getDebitAmount() != null ? e1.getDebitAmount() : BigDecimal.ZERO; 

    e1.setCreditAmount(creditAmount.add(e2.getCreditAmount())); 
    e1.setDebitAmount(debitAmount.add(e2.getDebitAmount())); 
}; 

LedgerEntry totalsEntry = entries.stream().collect(LedgerEntry::new, ac, ac); 

Tất cả các phiên bản trước Java 8 bắt đầu trông hấp dẫn.

+1

Có thể thay thế 'LedgerEntry() 'mới bằng' LedgerEntry :: new' để có thể đọc được không? – CKing

+3

@CKing No. Đó là giá trị ban đầu, không phải là tham chiếu phương thức. –

+0

Lưu ý. Tôi giả định 'reduce' sẽ có một dạng quá tải mà phải mất một giao diện chức năng như một' nhà cung cấp' nhưng đoán không có điều đó. – CKing

1

Bạn cần phải quấn kết quả của bạn thành một Pair của một số loại:

stream 
     .parallel() 
     .reduce(new AbstractMap.SimpleEntry<>(BigDecimal.ZERO, BigDecimal.ZERO), 
        (entry, ledger) -> { 
         BigDecimal credit = BigDecimal.ZERO.add(entry.getKey()).add(ledger.getCreditAmount()); 
         BigDecimal debit = BigDecimal.ZERO.add(entry.getValue()).add(ledger.getDebitAmount()); 
         return new AbstractMap.SimpleEntry<>(credit, debit); 
        }, (left, right) -> { 
         BigDecimal credit = BigDecimal.ZERO.add(left.getKey()).add(right.getKey()); 
         BigDecimal debit = BigDecimal.ZERO.add(left.getValue()).add(right.getValue()); 
         return new AbstractMap.SimpleEntry<>(credit, debit); 
        })); 
Các vấn đề liên quan