Sum BigDecimals in Java 8

Simplify adding BigDecimal in java 8 with stream reduction operation

Detailed Video Notes

By nature BigDecimals are immutable which means once the object is created it cannot be modified so when it comes to performing arithmetic operations they aren't all that friendly or performant. There are lots of libraries dealing with primitives such as Guava's math utilities but in this tutorial we will show how java 8 has simplified summing a series of BigDecimals from a collection.

Setup

[0:20]

Creating a project from maven archetype and importing it into eclipse we will update junit. Initializing an Arraylist of type BigDecimals we will add elements in the setUp method which have a sum of 15.

List<BigDecimal> bigDecimalsValues = new ArrayList<>();

@Before
public void setUp () {
    bigDecimalsValues.add(new BigDecimal(1));
    bigDecimalsValues.add(new BigDecimal(2));
    bigDecimalsValues.add(new BigDecimal(3));
    bigDecimalsValues.add(new BigDecimal(4));
    bigDecimalsValues.add(new BigDecimal(5));

}

Straight up java

[0:32]

Let's recap what this looks like in a java version prior to java 8 (5, 6, 7). You will need to create an object to store the sum value and iterate over the ArrayList with for loop. When you call the .add it produces a new object in which we need to set addBigDecimals equal to the result. Using junit assert statement we will validate the result is equal to 15.

@Test
public void add_big_decimals_java() {

    BigDecimal addBigDecimals = new BigDecimal(0);

    for (BigDecimal value : bigDecimalsValues) {
        addBigDecimals = addBigDecimals
                .add(new BigDecimal(value.intValue()));
    }

    assertEquals(new BigDecimal(15), addBigDecimals);
}

Using java 8

[0:53]

Java 8 introduced terminal operations such as average, sum, min, max, and count which wraps the general purpose Stream.reduce each in their own way. Converting an arraylist to a stream will enable us to call the general purpose reduce method passing in BigDecimal.ZERO as the identify and the BigDecimal::add accumulator method.

@Test
public void add_big_decimals_java8() {

    BigDecimal sumOfBigDecimals = bigDecimalsValues.stream().reduce(
            BigDecimal.ZERO, BigDecimal::add);

    assertEquals(new BigDecimal(15), sumOfBigDecimals);
}

Removing null values

[1:17]

Next, if you are dealing with null values within your list of BigDecimals most likely you will hit a NullPointerException when you try to add them all together. Using a stream we can filter null values prior to reduction operation to avoid an error. If you don't have the luxury of java 8 yet, you could use guava and filter null references from your arraylist.

@Test
public void add_big_decimals_java8_null() {

    bigDecimalsValues.add(null);

    BigDecimal sumOfBigDecimals = bigDecimalsValues.stream()
            .filter(Objects::nonNull)
            .reduce(BigDecimal.ZERO, BigDecimal::add);

    assertEquals(new BigDecimal(15), sumOfBigDecimals);
}

Summing object field

[1:42]

In our last snippet we will sum fields of a given object of type BigDecimal. First we will create an object Car which has a field miles of type BigDecimal. Declaring miles as a BigDecimal is for demonstration purposes as you likely wouldn't do this in the real world. Next we will initialize a list with the diamond operator adding Car objects. It is important to note that we added a null car object and initialize a car object with a null miles value.

Once we add values to the vehicle object we will want to write code to sum all values within a list using java 8 syntax. Remember we first need to filter null Car and car objects with miles set to null. Next calling .map with return the stream of BigDecimals and just like before will reduce the values and sum up the elements

class Car {

    private BigDecimal miles;

    public Car(BigDecimal miles) {
        super();
        this.miles = miles;
    }

    public BigDecimal getMiles() {
        return miles;
    }

    public void setMiles(BigDecimal miles) {
        this.miles = miles;
    }
}

@Test
public void add_big_decimals_java8_object() {

    List<Car> vehicles = new ArrayList<>();
    vehicles.add(null);
    vehicles.add(new Car(null));
    vehicles.add(new Car(new BigDecimal("10")));
    vehicles.add(new Car(new BigDecimal("20")));

    BigDecimal sumOfBigDecimals = vehicles.stream()
            .filter(Objects::nonNull)
            .filter(c -> c.getMiles() != null)
            .map(Car::getMiles)
            .reduce(BigDecimal.ZERO, BigDecimal::add);

    assertEquals(new BigDecimal(30), sumOfBigDecimals);
}

Thanks for joining in today's level up, have a great day!