我有一个List<LedgerEntry> ledgerEntries,我需要计算creditAmount和debitAmount的总和。

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

看来我要遍历List两次。有没有一种方法可以一劳永逸地完成此工作,而不必两次重复列出清单?

Java 8之前的版本
BigDecimal creditTotal = BigDecimal.ZERO;
BigDecimal debitTotal = BigDecimal.ZERO;
for(LedgerEntry entry : ledgerEntries){
  creditTotal = creditTotal.add(entry.getCreditAmount());
  debitTotal = debitTotal.add(entry.getDebitAmount());
}

最佳答案

您可以减少到总计条目:

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之前的版本突然开始显得强大起来。

07-27 13:35