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

The problem with this method is that it always returns zero. It looks like .forEach() is not consuming it's argument. However if I write it the way below, everything is working fine. Anyone got idea why the first method is not working?

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;
    }
Stefan Zobel
  • 3,182
  • 7
  • 28
  • 38
Virx
  • 105
  • 2
  • 11
  • 4
    Maybe because BigDecimal is immutable, so it returns a new instance when calling add instead of updating the original one? – Koekje Sep 07 '16 at 08:55
  • 1
    @Eran: It's a `BigDecimal`, which is the problem here. – fabian Sep 07 '16 at 08:55
  • 1
    @fabian I missed that, thanks. Actually it's BigDecimal, but same issue. – Eran Sep 07 '16 at 08:57
  • Possible duplicate of [Adding up BigDecimals using Streams](http://stackoverflow.com/questions/22635945/adding-up-bigdecimals-using-streams) – Didier L Sep 07 '16 at 20:01

3 Answers3

5

BigDecimal is immutable, so your forEach is calling add, but not doing anything with the result. reduce is the right stream operator in this case.

If you see Adding up BigDecimals using Streams. You should use .reduce(BigDecimal.ZERO, BigDecimal::add), which would make your loop body:

interest = i.getTransactions().stream()
                .map(Transaction::getAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
Community
  • 1
  • 1
davidsheldon
  • 38,365
  • 4
  • 27
  • 28
4

Since BigDecimal is immutable, calling add for it won't change the value. It instead returns a new BigDecimal. forEach simply ignores any values returned.

interest will always keep it's initial value of BigDecimal.ZERO.

In contrast to that reduce combines the elements using a given BinaryOperator. BigDecimal::add is actually a short form for (a, b) -> a.add(b) and this operator will be applied to combine all stream elements.

fabian
  • 80,457
  • 12
  • 86
  • 114
1

As the accepted answer explains, BigDecimal.add does not modify the instance, but returns a new value and your first variant is not using the result. As an addendum, the same would happen if you don’t use the result of reduce in you second variant, but there, you not only pass the result to an invocation of add on the previous value, you also care to assign the result of that subsequent add to your local variable.

But it’s worth noting that your mixture of loop and stream operation in inconsistent. You can express the entire operation as a single Stream operation:

BigDecimal getInterest(List<Investment> investments) {
    return investments.stream()
        .flatMap(i -> i.getTransactions().stream())
        .map(Transaction::getAmount)
        .reduce(BigDecimal.ZERO, BigDecimal::add);
}
Community
  • 1
  • 1
Holger
  • 285,553
  • 42
  • 434
  • 765