2016-09-07 58 views
1
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; 
    } 
+4

也許是因爲BigDecimal是不可變的,因此它在調用add時而不是更新原始調用時返回新實例? – Koekje

+1

@Eran:這是'BigDecimal',這是這裏的問題。 – fabian

+1

@fabian我錯過了,謝謝。其實它是BigDecimal,但同樣的問題。 – Eran

回答

4

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

由於BigDecimal是不可改變的,呼籲add因爲它不會改變的價值。它會返回一個新的BigDecimalforEach只是忽略返回的任何值。

interest將始終保持其初始值BigDecimal.ZERO

相比之下,reduce結合使用給定BinaryOperator的元素。 BigDecimal::add實際上是(a, b) -> a.add(b)的簡寫形式,此運算符將應用於合併所有流元素。

1

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