Java 8在一次迭代中求和两个对象属性

Kri*_*han 7 java foreach lambda java-8 java-stream

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

class LedgerEntry{
 private BigDecimal creditAmount;
 private BigDecimal debitAmount;

 //getters and setters
}
Run Code Online (Sandbox Code Playgroud)

我实现了这个,

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
Run Code Online (Sandbox Code Playgroud)

这看起来像是在两次迭代List.有没有办法一次性完成这项工作,而无需两次蒸发列表?

Pre Java 8版本

BigDecimal creditTotal = BigDecimal.ZERO;
BigDecimal debitTotal = BigDecimal.ZERO;
for(LedgerEntry entry : ledgerEntries){
  creditTotal = creditTotal.add(entry.getCreditAmount());
  debitTotal = debitTotal.add(entry.getDebitAmount());
}
Run Code Online (Sandbox Code Playgroud)

Rob*_*sen 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;
});
Run Code Online (Sandbox Code Playgroud)

更新

在评论中正确地指出,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);
Run Code Online (Sandbox Code Playgroud)

突然之前,Java 8之前版本开始具有强大的吸引力.

  • @CKing那是因为**这个答案显示了`reduce()`**的无效使用:第一个参数是`identity`,而`BinaryOperator`不应该修改它.它应该返回另一个对象.收集操作需要修改他们的输入(事实上他们将`Supplier`作为参数). (5认同)
  • @CKing No.这是初始值,而不是方法参考. (3认同)