Java 8 Sum two object properties in one iteration

I have a List<LedgerEntry> ledgerEntries and I need to calculate the amounts of creditAmount and debitAmount.

 class LedgerEntry{ private BigDecimal creditAmount; private BigDecimal debitAmount; //getters and setters } 

I implemented this as

 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 

Looks like I'm repeating the list twice. Is there a way to do this at a time without skipping the list twice?

Java 8 Preview

 BigDecimal creditTotal = BigDecimal.ZERO; BigDecimal debitTotal = BigDecimal.ZERO; for(LedgerEntry entry : ledgerEntries){ creditTotal = creditTotal.add(entry.getCreditAmount()); debitTotal = debitTotal.add(entry.getDebitAmount()); } 
+7
java lambda foreach java-8 java-stream
source share
2 answers

You can reduce to a record of totals:

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

Update

The comments correctly pointed out that reduce() should not change the initial value of the identifier and that collect() should be used for mutable reductions. Below is the version using collect() (using the same BiConsumer for both battery and combiner). It also considers potential creditAmount if creditAmount and / or debitAmount not set.

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

The flash version of pre-Java 8 is starting to look attractive.

+12
source share

You need to wrap the results in 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); })); 
+1
source share

All Articles