How do I sum a BigDecimal property of a list of objects using Java Stream API?

If we have a list of objects, and we want to sum a BigDecimal property of these objects, we can achieve this using the Java Stream API. This API provides a clean and efficient way to process collections of objects. To sum the BigDecimal amounts, you can use the map and reduce methods of the Stream API.

As an example, we have a class named Transaction with a BigDecimal property named amount. We have a list of Transaction objects, and we want to calculate the total sum of the amount properties.

In the code snippet below we do the following:

  • Creating Transactions: We create a list of Transaction objects, each with a different BigDecimal amount.
  • Filter Transactions and its amount: We filter to exclude the null transaction and null transaction amount.
  • Mapping to Amounts: We use the map method to convert each Transaction object to its amount property.
  • Summing the Amounts: The reduce method takes two parameters: an identity value (BigDecimal.ZERO) and an accumulator function (BigDecimal::add). The accumulator function adds each BigDecimal in the stream to the running total.
  • Printing the Result: Finally, we print the total sum of the amounts.
package org.kodejava.stream;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;

public class BigDecimalSumExample {
    public static void main(String[] args) {
        // Create a list of transaction objects
        List<Transaction> transactions = Arrays.asList(
                new Transaction(new BigDecimal("10.50")),
                null,
                new Transaction(new BigDecimal("30.25")),
                new Transaction(null),
                new Transaction(new BigDecimal("11.49"))
        );

        // Sum the amount properties using stream
        BigDecimal totalAmount = transactions.stream()
                // Filter out null Transaction objects and Transaction objects
                // with null amounts
                .filter(t -> t != null && t.getAmount() != null)
                .map(Transaction::getAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        // Print the result
        System.out.println("Total Amount: " + totalAmount);
    }

    static class Transaction {
        private final BigDecimal amount;

        public Transaction(BigDecimal amount) {
            this.amount = amount;
        }

        public BigDecimal getAmount() {
            return amount;
        }
    }
}

Below is another example, we want to sum just a List<BigDecimal> values. To sum the values we can use the reduce method as shown in the code snippet below.

package org.kodejava.stream;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

public class BigDecimalListSumExample {
    public static void main(String[] args) {
        // Create a list of BigDecimal values
        List<BigDecimal> amounts = Arrays.asList(
                new BigDecimal("10.50"),
                new BigDecimal("20.75"),
                new BigDecimal("30.25"),
                null,
                new BigDecimal("11.49")
        );

        // Sum the BigDecimal values using stream
        BigDecimal totalAmount = amounts.stream()
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        // Print the result
        System.out.println("Total Amount: " + totalAmount);
    }
}

Using Java Stream API to sum a BigDecimal property of a list of objects or a list of BigDecimal values are both concise and efficient. The map and reduce methods streamline the process, making our code more readable and maintainable. This approach can be applied to various scenarios where we need to aggregate data from a list of objects.

How do I use map, filter, reduce in Java Stream API?

The map(), filter(), and reduce() methods are key operations used in Java Stream API which is used for processing collection objects in a functional programming manner.

Java Streams provide many powerful methods to perform common operations like map, filter, reduce, etc. These operations can transform and manipulate data in many ways.

  • map: The map() function is used to transform one type of Stream to another. It applies a function to each element of the Stream and then returns the function’s output as a new Stream. The number of input and output elements is the same, but the type or value of the elements may change.

Here’s an example:

package org.kodejava.basic;

import java.util.Arrays;
import java.util.List;

public class MapToUpperCase {
    public static void main(String[] args) {
        List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1");
        myList.stream()
                .map(String::toUpperCase)
                .sorted()
                .forEach(System.out::println);
    }
}

Output:

A1
A2
B1
C1
C2

Another example to use map() to convert a list of Strings to a list of their lengths:

package org.kodejava.basic;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class MapStringToLength {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("Java", "Stream", "API");
        List<Integer> lengths = words
                .stream()
                .map(String::length)
                .collect(Collectors.toList());

        System.out.println("Lengths = " + lengths);
    }
}

Output:

Lengths = [4, 6, 3]
  • filter: The filter() function is used to filter out elements from a Stream based upon a Predicate. It is an intermediate operation and returns a new stream which consists of elements of the current stream which satisfies the predicate condition.

Here’s an example:

package org.kodejava.basic;

import java.util.Arrays;
import java.util.List;

public class FilterStartWith {
    public static void main(String[] args) {
        List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1");
        myList.stream()
                .filter(s -> s.startsWith("c"))
                .map(String::toUpperCase)
                .sorted()
                .forEach(System.out::println);
    }
}

Output:

C1
C2

Another example:

package org.kodejava.basic;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class FilterEvenNumber {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
        List<Integer> evens = numbers
                .stream()
                .filter(n -> n % 2 == 0)
                .collect(Collectors.toList());

        System.out.println("Even numbers = " + evens);
    }
}

Output:

Even numbers = [2, 4, 6]
  • reduce: The reduce() function takes two parameters: an initial value, and a BinaryOperator function. It reduces the elements of a Stream to a single value using the BinaryOperator, by repeated application.

Here’s an example:

package org.kodejava.basic;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class ReduceSum {
    public static void main(String[] args) {
        List<Integer> myList = Arrays.asList(1, 2, 3, 4, 5);
        Optional<Integer> sum = myList
                .stream()
                .reduce((a, b) -> a + b);

        sum.ifPresent(System.out::println);
    }
}

Output:

15

In the above example, the reduce method will sum all the integers in the stream and then ifPresent is simply used to print the sum if the Optional is not empty.

All these operations can be chained together to build complex data processing pipelines. Furthermore, they are “lazy”, meaning they don’t perform any computations until a terminal operation (like collect()) is invoked on the stream.