Accumulate inside forEach Java 8

Viewed 291

How can I accumulate inside a forEach? this is my code:

public Double accumulate() {
        Double accum = 0d;

        numbers.stream().forEach(p-> {

            // if condition
                accum = accum + numbers.getAmount();
            // else
                accum = accum + numbers.getAmountWithInterest();
        });

        return accum;
    }

Maybe I should use map instead of forEach, I tried a couple of things but it didn't work. Thanks

2 Answers

I do not think it is a good idea to make side effect when using lambda. It is a bad way of mixing functional and imperative programming. You can do it easier by

numbers.stream().mapToInt(p-> {
    // if condition
        return numbers.getAmount();
    // else
        return numbers.getAmountWithInterest();
}).sum();

You can use mapToDouble and sum instead of forEach with Java Streams:

@Test
public void sumOfDoubleWithStreams()
{
    List<Amount> numbers =
        Arrays.asList(new Amount(10.0, 0.0), new Amount(20.0, 0.05), new Amount(30.0, 0.1));
    double sum = numbers.stream()
        .mapToDouble(
            amount -> amount.hasInterest() ? amount.getAmountWithInterest() : amount.getAmount())
        .sum();
    Assert.assertEquals(64.0d, sum, 0.0d);
}

If you really want to use forEach, you need to have an object that can be mutated inside of the forEach call. You can use AtomicDouble here, or another class like DoubleSummaryStatistics or your own accumulating class.

@Test
public void sumOfDoubleWithForEach()
{
    List<Amount> numbers =
        Arrays.asList(new Amount(10.0, 0.0), new Amount(20.0, 0.05), new Amount(30.0, 0.1));
    AtomicDouble sum = new AtomicDouble();
    numbers.forEach(amount ->
        sum.addAndGet(amount.hasInterest() ? amount.getAmountWithInterest() : amount.getAmount()));
    Assert.assertEquals(64.0d, sum.get(), 0.0d);
}

If you are open to using a third-party library, you can use sumOfDouble on any container from Eclipse Collections.

@Test
public void sumOfDouble()
{
    MutableList<Amount> numbers = 
        Lists.mutable.with(new Amount(10.0, 0.0), new Amount(20.0, 0.05), new Amount(30.0, 0.1));
    double sum = numbers.sumOfDouble(
        amount -> amount.hasInterest() ? amount.getAmountWithInterest() : amount.getAmount());
    Assert.assertEquals(64.0d, sum, 0.0d);
}

I created an Amount class to hold the amount and interest rate:

public static final class Amount
{
    private final double amount;
    private final double interest;

    public Amount(double amount, double interest)
    {
        this.amount = amount;
        this.interest = interest;
    }

    public double getAmountWithInterest()
    {
        return this.amount * (1.0 + this.interest);
    }

    public boolean hasInterest()
    {
        return this.interest > 0.0d;
    }

    public double getAmount()
    {
        return amount;
    }
}

Note: I am a committer for Eclipse Collections.

Related