Skip to main content

Sum a List of numbers in Java

Escrito por:
wordpress-sync/Java-engineering-feature

22 de maio de 2020

0 minutos de leitura

Every now and then, I need to do some basic stuff in Java and I wonder what is the best way to this. This happened to me a few days ago! I needed to simply get the sum of a List of numbers and I found out there are a number of ways — pun intended — to do this.

The old-fashioned approach

We can create a simple loop to do this. I am using Java 11 so, forgive me if you are using, for example, Java 8,  and the List.of and var does not work in your case. Nonetheless, I believe you’ll still get the point.

var listOfNumbers = List.of(1,2,3,4,5,6,7,8,9,10);

var sum = 0;
for (int i = 0; i < listOfNumbers.size() ; i++) {
    sum += listOfNumbers.get(i);
}

Obviously, since Java 5, we have enhancements for loops so, I can rewrite the same code like this.

var listOfNumbers = List.of(1,2,3,4,5,6,7,8,9,10);

var sum = 0;
for (int number : listOfNumbers) {
    sum += number;
}

The difference is subtle. However, it is already more expressive as it says something like "of each number coming from listOfNumbers I want to do the following ...".

The Java Stream approach

People who know me, know that I was brainwashed, during my university days, with programming in Haskell. This means I have a lot of love for pure functional programming. Not that Java can handle that ?, but the expressiveness of functional programming is somewhat available using the stream API.

With the stream API in Java, we can execute the MapReduce programming model. For the issue I am trying to solve here, I do not need to map as the numbers will stay as they are. I do, however, have to reduce the List into a single number, the sum.

Collect

In probably 99% of the cases, we use the collect function with the standard toList() collector to reduce our stream back into a List.Similar to this:

       var time2ToList = listOfNumbers.stream()
                .map(i -> i * 2)
                .collect(Collectors.toList());

However, there is more to life than collecting a stream back into a List. Browsing the Collectors library you can find functions like summingInt(), summingDouble() and summingLong().You can use these functions to collect (or reduce) the List into the sum.

The summmingInt function does require a function that turns the input you have into an int. In this case, I can simply use "identity function". The functioni -> i will be sufficient.

        var listOfNumbers = List.of(1,2,3,4,5,6,7,8,9,10);
        var sum = listOfNumbers.stream()
                    .collect(Collectors.summingInt(i -> i));

This identity function might look silly so, you can use Integer.intValue() instead.

       var listOfNumbers = List.of(1,2,3,4,5,6,7,8,9,10);
       var sum = listOfNumbers.stream()
                .collect(Collectors.summingInt(Integer::intValue));

When I do this, my IDE—IntelliJ IDEA in my case—advises me to refactor this and use the mapToInt() function like seen below:

       var listOfNumbers = List.of(1,2,3,4,5,6,7,8,9,10);
       var sum = listOfNumbers.stream()
                .mapToInt(Integer::intValue).sum();

Technically what we do here is mapping every item to an int, what it already is ¯\(ツ)/¯  right, and reduce it with the sum() function.

It gets more clear if you look at the inferred types. You simply cannot have a List of primitives. So, the List is a list of Integer (the Object). This means that every item in the list needs to get back to the primitive int to make the sum() possible. The previous example with the identity function in the collector works because of Java unboxing.

If you prefer using primitive Lists in Java, I suggest taking a look at the Eclipse Collections library.

Reduce

Reduction in Java is achieved by a couple of function in the stream API.In addition to collect(), there is also the obviously-named function reduce().

       var listOfNumbers = List.of(1,2,3,4,5,6,7,8,9,10);
       var sum = listOfNumbers.stream()
                .reduce(0 , (num1, num2) -> num1 + num2);

The reduce function in this case takes a starting point and BiFunction lambda expression. The BiFunction is applied to the starting point and the first number,he result of the function is applied to the second number, and so on.

The code above does something like this:0 + 11 + 23 + 36 + 4etc …

Now, you can omit the starting point 0. However, the reduce function will return an Optional in this case as the List it tries to reduce might be empty.

Conclusion

As you can see, there are multiple ways to solve this problem. Without any doubt, people will come up with even more exotic ways to solve this. My personal favorite is the reduce() option. For me, this is the most expressive and pure solution in Java. I simply want to reduce a list to a single number and don't need to care of the transformations from boxed types to primitives. Furthermore, I can reuse this approach when I need to reduce a List of other types by writing a reduction lambda function that fits my needs.

Publicado em:
wordpress-sync/Java-engineering-feature