不适用于循环的流
问题描述:
BigDecimal getInterest(List<Investment> investments) {
BigDecimal interest = BigDecimal.ZERO;
for (Investment i: investments) {
i.getTransactions().stream()
.map(Transaction::getAmount)
.forEach(interest::add);
}
return interest;
}
此方法的问题是它始终返回零。它看起来像.forEach()
没有消耗它的论点。但是,如果我按照下面的方式编写它,一切工作正常。任何人都知道为什么第一种方法不起作用?不适用于循环的流
BigDecimal getInterest(List<Investment> investments) {
BigDecimal interest = BigDecimal.ZERO;
for (Investment i: investments) {
interestPaid = interest.add(i.getTransactions().stream()
.map(Transaction::getAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
}
return interest;
}
答
BigDecimal
是不变的,所以你forEach
呼唤add
,但与结果做任何事情。在这种情况下,reduce
是正确的流操作符。
如果看到Adding up BigDecimals using Streams。您应该使用.reduce(BigDecimal.ZERO, BigDecimal::add)
,这将使你的循环体:
interest = i.getTransactions().stream()
.map(Transaction::getAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
答
由于BigDecimal
是不可改变的,呼吁add
因为它不会改变的价值。它会返回一个新的BigDecimal
。 forEach
只是忽略返回的任何值。
interest
将始终保持其初始值BigDecimal.ZERO
。
相比之下,reduce
结合使用给定BinaryOperator
的元素。 BigDecimal::add
实际上是(a, b) -> a.add(b)
的简写形式,此运算符将应用于合并所有流元素。
答
由于accepted answer解释,BigDecimal.add
不会修改该实例,但会返回一个新值并且您的第一个变体未使用结果。作为一个附录,如果你不在第二个变体中使用reduce
的结果,也会发生同样的情况,但是在那里,你不仅仅将结果传递给前一个值的调用add
,还需要分配结果随后add
到你的本地变量。
但值得注意的是,你的混合循环和流操作不一致。您可以将整个操作表示为单个Stream操作:
BigDecimal getInterest(List<Investment> investments) {
return investments.stream()
.flatMap(i -> i.getTransactions().stream())
.map(Transaction::getAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
也许是因为BigDecimal是不可变的,因此它在调用add时而不是更新原始调用时返回新实例? – Koekje
@Eran:这是'BigDecimal',这是这里的问题。 – f*
@f*我错过了,谢谢。其实它是BigDecimal,但同样的问题。 – Eran