Three Ways to Sum a Stream of Numbers

Introduction

In this post, we will see three ways to sum a stream of numbers using Java streams. We will use a stream created from a list of integers and sum the integers using various approaches. 

Option 1 – Using mapToInt on the Stream

A stream can contain elements of any type, whereas an IntStream represents a sequence of primitive int-valued elements. We can convert a stream of arbitrary type to an IntStream by using the mapToInt method on the stream.

mapToInt method signature:

IntStream mapToInt(ToIntFunction<? super T> mapper)
The mapToInt method takes a ToIntFunction. A ToIntFunction is a functional interface which maps any element to a primitive int value. It has a single method called applyAsInt as shown below.
int applyAsInt(T value)
When we pass an instance of ToIntFunction to the mapToInt method, it applies this function to each element in the stream and returns an IntStream. An IntStream has a sum() method to sum all the elements in the IntStream (which are primitive integers). 
List<Integer> integers = List.of(1, 2, 3, 4, 5);
int sum = integers.stream()
                .mapToInt(Integer::intValue)
                .sum();

Note: List.of method was added in Java 9 as part of the many Collection factory methods. If you are using Java 8 or lower, use Arrays.asList instead.

In the above example, calling stream() on the collection returns a stream of integers (Stream<Integer>). Integer::intValue is a method reference for the lambda expression `element -> element.intValue()`. It unboxes an Integer to a primitive int and thus it satisfies the ToIntFunction method signature (i.e., it takes an Integer object and returns a primitive int). Since we have an IntStream, we call the sum method on it to find the sum of elements in the (Int)Stream.

However, there is another way we could have written the mapToInt to make advantage of the automatic unboxing.

int sum = integers.stream()
                .mapToInt(e -> e)
                .sum();

The lambda expression e -> e returns the same Integer instance. But, under the hood, automatic unboxing takes place and hence this is equivalent to calling the intValue method on the Integer object.

Sum a Java Stream as a long or double

We can use the mapToLong and mapToDouble methods to map an element to a long and a double, respectively. It will return a LongStream and a DoubleStream, respectively. Then, calling sum() on the LongStream or DoubleStream will return a double or a long.

Note:

  • mapToLong takes a ToLongFunction. The ToLongFunction has a single method called applyAsLong to map an element to a primitive long.
  • mapToDouble takes a ToDoubleFunction. The ToDoubleFunction has a single method called applyAsDouble to map an element to a primitive double.
long sumAsLong = integers.stream()
        .mapToLong(Integer::intValue)
        .sum();

double sumAsDouble = integers.stream()
        .mapToDouble(Integer::intValue)
        .sum();

Option 2 – Sum a Java Stream by reducing the stream

The second way by which we can sum a stream of numbers is to use the reduce method on the stream. The reduce method performs a reduction on the stream elements using the provided 

  • identity element and 
  • an accumulator function
The identity element when passed to the accumulator function with some other element (say e) must return the other element (e) as the result. 
Example: The identity element for addition is 0, because 0 + e (any element) gives us e. The identity for multiplication is 1 (1 x e = e).
 
The accumulator function is represented by a BinaryOperator. A BinaryOperator is a BiFunction i.e., it takes two elements of a type and returns a single result of the same type.

int sum = integers.stream()
        .reduce(0, (a, b) -> a + b);

We pass an identity value of 0 for the accumulator function. The accumulator function takes two elements of type int and returns the sum of them (an int). Since the Integer’s sum method takes two integers and returns the sum of it, we can use a method reference in its place, as shown below.

int sum = integers.stream()
        .reduce(0, Integer::sum);

I have a separate post on Java Stream reduce method where I talk in depth on the three overloaded Stream#reduce methods.

Option 3 – Using Collectors.summingInt

The Collectors.summingInt is a Collector that sums the primitive integers. It accepts a ToIntFunction (the same type passed for the mapToInt method) to convert an element in the stream to a primitive int.

We can use the Stream#collect along with Collectors.summingInt as shown below.

int sum = integers.stream()
        .collect(Collectors.summingInt(Integer::intValue));

However, this is a roundabout way of computing a stream’s sum. The Intellij IDE suggests to refactor the above to use mapToInt (the first method).

Sum a stream obtained from a Map

So far, we have learnt three ways to sum a stream of numbers where the stream was created from a list. If we had a map, then we just have to create a stream of integers out of the map’s values or keys and use any of the methods we’ve seen.

Let us say we have the below map.

Map<Integer, String> map = Map.of(
        1, "abc",
        2, "defg",
        3, "pqrs",
        4, "uvw",
        5, "xyz");

We want to find the sum of the lengths of all even length strings.

int result = map.values().stream()
        .filter(string -> string.length() % 2 == 0)
        .mapToInt(String::length)
        .sum();
System.out.println(result); //8

We create a Stream<String> from the map values, filter the strings whose length is of even length. Then in the mapToInt, we map a string element into an integer, i.e., the length of the string. Finally, we call sum() on the stream to get the result.

Conclusion

In this post, we learnt three ways to sum a stream of integers in Java. We looked at the mapToInt, reduce, and collect methods from the Stream.

References

Baeldung Stream Sum
How to sum a list of integers with java streams – Stackoverflow post

This Post Has 2 Comments

  1. Roman

    in the last example, the result of (int result) will be 4.
    only one row matches after filtering on key 2.

    System.out.println(result); // 4

    1. admin

      Thank you. I’ve updated the example.

Leave a Reply