2017-02-22 198 views
7

我有一個List<LedgerEntry> ledgerEntries,我需要計算creditAmount和debitAmount的總和。Java 8在一次迭代中求和兩個對象屬性

class LedgerEntry{ 
private BigDecimal creditAmount; 
private BigDecimal debitAmount; 

//getters and setters 
} 

我已經實現了本作,

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 

這看起來像我遍歷列表的兩倍。有沒有辦法一次完成這項工作,而無需兩次蒸汽清單?

前的Java版本8

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

你爲什麼要使用流?您的「Pre Java 8」版本也是100%有效的Java 8,並且(因爲它實際上並沒有做任何事情,因爲BigDecimal是不可變的,所以它的可讀性和可維護性更高且可維護​​性更高)試圖一次計算兩個和的解決方案。 – Hoopje

+0

@KrazyKalle:謝謝。編輯 – Krishan

+0

@KrazyKalle。是。你認爲我的意思是括號內的句子(固定時......不可變)? – Hoopje

回答

12

你可以減少到總數的條目:

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; 
}); 

更新

在評論中有人正確地指出,reduce()不應該修改初始標識符價值,而collect()應該用於可變減價。以下是使用collect()(使用與累加器和組合器相同的BiConsumer)的版本。如果尚未設置creditAmount和/或debitAmount值,它還解決潛在的NPE問題。

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); 

突然之間預先的Java版本8開始看起來強大的吸引力。

+1

爲了便於閱讀,是否可以將'new LedgerEntry()'替換爲'LedgerEntry :: new'? – CKing

+3

@CKing號碼這是初始值,不是方法參考。 –

+0

已注意。我認爲'reduce'會有一個重載的形式,需要一個功能接口,比如'Supplier',但是猜測沒有這樣的事情。 – CKing

1

你需要用你的成果轉化爲某種形式的Pair

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); 
        }));