Explain Codes LogoExplain Codes Logo

Adding up BigDecimals using Streams

java
prompt-engineering
functions
collections
Nikita BarsukovbyNikita Barsukov·Sep 6, 2024
TLDR

Quick and dirty solution to sum BigDecimal values using streams applies the reduce() method:

BigDecimal sum = bigDecimals.stream() .reduce(BigDecimal.ZERO, BigDecimal::add);

Here bigDecimals signifies your collection of BigDecimal; stream() transforms it into a stream; and reduce() consolidates it all beginning from BigDecimal.ZERO, leveraging BigDecimal::add method to accumulate the total sum.

Operational scenarios

Ever found yourself working with complex objects and pulling hairs over calculations? Fear not, Java provides a simple way using the map function combined with reduce. For example, you have a list of invoices and you want to calculate the total of all invoices:

// The not-so-secret sauce to adding invoices BigDecimal invoiceTotalSum = invoices.stream() .map(Invoice::total) .reduce(BigDecimal.ZERO, BigDecimal::add);

This piece of code applies the Invoice::total method on each invoice, converts it into the relevant amount, and then adds it all up to produce the grand total. But beware! Life is full of surprises like nulls that you might encounter along the way. Dismiss this headache using filter(Objects::nonNull):

// Ensure no pesky nulls sneak in! BigDecimal invoiceTotalSum = invoices.stream() .filter(Objects::nonNull) .map(Invoice::total) .reduce(BigDecimal.ZERO, BigDecimal::add);

This code works to filter out the nulls before the rest of the stream operations are applied, thus preventing any unwanted **NullPointerException**s.

Ensuring precision

Creative code requires statistical data to ensure its efficiency, Eclipse Collections comes to the rescue providing summarizingBigDecimal() for your needs:

// Because we all need some stats in our lives MutableBigDecimalSummaryStatistics stats = bigDecimals.stream() .collect(Collectors2.summarizingBigDecimal(identity()));

Here, MutableBigDecimalSummaryStatistics is like the Swiss army knife of statistical data, providing you with the sum, average, count, min, and max. And always remember, when performing BigDecimal calculations, use the adapted methods and avoid converting to double or float to safeguard precision.

Cauldron of Customizations

When dealing with BigDecimal values, customization is crucial for precision, performance, and catering to your specific needs. In such cases, a custom Collector magically solves our problem:

// Brewing up a custom Collector Collector<BigDecimal, ?, BigDecimal> summingUp = Collector.of( () -> new BigDecimal[]{BigDecimal.ZERO}, (a, t) -> a[0] = a[0].add(t), // Kind of like Dumbledore's Pensieve (a, b) -> new BigDecimal[]{a[0].add(b[0])}, // Now we are adding memeries! a -> a[0], Collector.Characteristics.IDENTITY_FINISH );

This custom Collector, named summingUp, is designed to focus on maintaining precision during the accumulation process.

Check for hidden traps

Steering clear of calculations errors and nulls is as important as finding buried treasure! Here's how to safeguard your calculations:

// When dealing with invisible Dementors (read: nulls) Function<Invoice, BigDecimal> safeInvoiceTotal = invoice -> (invoice != null && invoice.getUnitPrice() != null && invoice.getQuantity() != null) ? invoice.total() : BigDecimal.ZERO; // Because sometimes, zero is the hero! BigDecimal safeSum = invoices.stream() .map(safeInvoiceTotal) .reduce(BigDecimal.ZERO, BigDecimal::add);

This safeInvoiceTotal map function wards off the Dementors, ensuring no null values slip in your calculation pipeline.