Search code examples
javajava-8sumjava-streambigdecimal

Summing a map of doubles into an aggregated BigDecimal with Java Streams


I'm trying to use the Java 8 Stream API mapToDouble method like so:

BigDecimal totalCost = order.getOrderlineList().stream()
    .mapToDouble(Orderline::getPrice)
    .sum();

The problem is that Orderline::getPrice returns a BigDecimal, not a Double. Hence the attempt above fails to compile (Bad return type in method reference: cannot convert java.math.BigDecimal to doubele).

Seeing that Orderline#price is a BigDecimal, how can I use the Stream API (and either mapToDouble or something similar) to get my totalCost?


Solution

  • You should add BigDecimals using BigDecimal.add() instead of converting them to double and back again to avoid rounding errors. To sum all prices you can use Stream.reduce():

    BigDecimal bigDecimal = order.getOrderLines().stream()
            .map(OrderLine::getPrice)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    

    From the docs of Stream.reduce():

    Performs a reduction on the elements of this stream, using the provided identity value and an associative accumulation function, and returns the reduced value. This is equivalent to:

    T result = identity;
    for (T element : this stream)
        result = accumulator.apply(result, element)
    return result;
    

    BigDecimal::add is a short form of (a, b) -> a.add(b), which simply returns a + b. For addition the identity element is 0 or BigDecimal.ZERO.