Guide to Java 8 Collectors: summingDouble(), summingLong() and summingInt()

Introduction

A stream represents a sequence of elements and supports different kinds of operations that lead to the desired result. The source of a stream is usually a Collection or an Array, from which data is streamed from.

Streams differ from collections in several ways; most notably in that the streams are not a data structure that stores elements. They're functional in nature, and it's worth noting that operations on a stream produce a result and typically return another stream, but do not modify its source.

To "solidify" the changes, you collect the elements of a stream back into a Collection.

Many simple mathematical operations and concepts find their usage in programming, and most often these are just as straightforward in programming as they are in math itself. Summing up elements isn't indifferent to this, and it's usage is often overlooked because there are many ways to utilize this simple task.

In this guide, we'll take a look at how to sum all elements in a collection in Java, using summingDouble(), summingLong() and summingInt().

Note: It's worth noting that you can sum the elements themselves, if they're summable, or reduce them to a numerical representation and then sum the reductions, if they aren't.

Collectors and Stream.collect()

Collectors represent implementations of the Collector interface, which implements various useful reduction operations, such as accumulating elements into collections, summarizing elements based on a specific parameter, etc.

All predefined implementations can be found within the Collectors class.

You can also very easily implement your own collector and use it instead of the predefined ones, though - you can get pretty far with the built-in collectors, as they cover the vast majority of cases in which you might want to use them.

To be able to use the class in our code we need to import it:

import static java.util.stream.Collectors.*;

Stream.collect() performs a mutable reduction operation on the elements of the stream.

A mutable reduction operation collects input elements into a mutable container, such as a Collection, as it processes the elements of the stream.

Definition of the summing_() Collectors

Within the Collectors class itself, we find an abundance of unique methods that deliver on the different needs of a user. One such group is made of summing methods - summingInt(), summingDouble() and summingLong().

Even though each of these methods work for a distinctive data type listed within their names, they all work fairly similarly, with minor differences:

public static <T> Collector<T,?,Integer> summingInt(ToIntFunction<? super T> mapper)

public static <T> Collector<T,?,Double> summingDouble(ToDoubleFunction<? super T> mapper)

public static <T> Collector<T,?,Long> summingLong(ToLongFunction<? super T> mapper)

Note: The generic T in the method signatures represents the type of the input elements we're working with.

Earlier we stated that these methods can only be used for numerical inputs. The predefined ToIntFunction, ToDoubleFunction and ToLongFunction from java.util.function enable us to do exactly such conversions - from object types to their primitive int, double, long types.

Before we begin diving into each of the individual methods, we'll be defining a class that we'll use for our examples alongside a few fields.

This class will be called Product:

public class Product {
    private String name;
    private Integer quantity;
    private Double price;
    private Long productNumber;

    // Constructor, getters and setters
}

The class has four fields of different types:

  • String name - we won't end up using this field since this is a non-numeric value, but in order to have meaningful examples we need to name our products.
  • Integer quantity - the number of products in our inventory.
  • Double price - the price of the product.
  • Long productNumber - a six-digit code for every of our products.

Alongside these four fields, we also have a simple constructor and getters for everything except the name of the product itself. Let's also instantiate our List of products in the main program:

List<Product> products = Arrays.asList(
        new Product("Milk", 37, 3.60, 12345600L),
        new Product("Carton of Eggs", 50, 1.20, 12378300L),
        new Product("Olive oil", 28, 37.0, 13412300L),
        new Product("Peanut butter", 33, 4.19, 15121200L),
        new Product("Bag of rice", 26, 1.70, 21401265L)
);

Note: Besides this class, which we'll have to reduce to a field in order to sum, we'll also be taking a look at examples of Lists consisting only of Integer, Double and Long elements.

Collectors.summingInt()

The summingInt() method returns a Collector that produces the sum of a integer-valued function applied to the input elements. In other words - it sums the integers in the collection and returns the result. In the case of no input elements, the return value is 0.

Let's start off with a basic example with a List of Integers:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Integer sum = numbers.stream().collect(Collectors.summingInt(Integer::intValue));
System.out.println("Sum: " + sum);

We apply the .stream() method to create a stream of Integer instances, after which we use the previously discussed .collect() method to collect the elements using summingInt(). The method itself, again, accepts the ToIntFunction, which can be used to reduce instances to an integer that can be summed.

Since we're using Integers already, we can simply pass in a method reference denoting their intValue, as no further reduction is needed:

Sum: 15

More often than not - you'll be working with lists of custom objects and would like to sum some of their fields. For instance, we can sum the quantities of each product in the productList, denoting the total inventory we have.

Free eBook: Git Essentials

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

In such a case, the we can use a method reference, such as Product::getQuantity as our ToIntFunction, to reduce the objects into a single integer each, and then sum these integers:

Integer sumOfQuantities = products.stream().collect(Collectors.summingInt(Product::getQuantity));
System.out.println("Total number of products: " + sumOfQuantities);

This results in:

Total number of products: 174

If you'd like to read more about reduction and reducing operations, which are typically at the core of summing and averaging operations, amongst other tasks - read our Java 8 Streams: Definitive Guide to reduce()!

Collectors.summingDouble()

In much the same way summingInt() returns the result of summed integer values - summingDouble() returns the result of summed double values.

This method varies from the summingInt() in one regard, however. The sum returned can vary depending on the order in which values are recorded, due to accumulated rounding errors. Values sorted by increasing order tend to produce more accurate results.

Note: If any value is a NaN or the sum at any point is NaN, the result will also be NaN.

Let's start off with a list of doubles:

List<Double> numbers = Arrays.asList(3.0, 5.5, 11.3, 40.3, 21.1);
Double sum = numbers.stream().collect(Collectors.summingDouble(Double::doubleValue));
System.out.println(sum);

After rounding off our output it prints out:

81.2

Again, we typically work with custom objects and not wrapper/primitive types. Prices of our products are expressed in doubles, so we could sum prices. If we were to buy a single instance of every product in the inventory, what would the price be?

Let's use summingDouble() to get a quote for the price:

Double sumOfPrices = products.stream().collect(Collectors.summingDouble(Product::getPrice));
System.out.println("The total sum of prices: " + sumOfPrices);

This gives us:

The total sum of prices: 47.69

If we wanted to get a little creative and count the total price of all of our products combined we can do that using this method as well. However, it does require beforehand calculations that makes this significantly easier:

List<Double> productPrices = new ArrayList<>();
for(Product product : products){
    productPrices.add(product.getPrice() * product.getQuantity());
}

Double sumOfPrices = productPrices.stream().collect(Collectors.summingDouble(Double::doubleValue));
System.out.println("Sum of all product prices : " + sumOfPrices);

Running this code gives us the following:

Sum of all product prices : 1411.67

Collectors.summingLong()

The third and last method from the summing group of methods is summingLong(). This method, as the previous two do, returns a Collector that produces the sum of a long-valued function applied to the input elements. If no elements are present, the result is 0:

List<Long> numbers = Arrays.asList(23L, 11L, 13L, 49L, 7L);
Long sum = numbers.stream().collect(Collectors.summingLong(Long::longValue));
System.out.println(sum);

This outputs:

103

Finally, our productNumber field is of type Long. Turns out, the numbers were carefully chosen to encode a secret message once split up and converted from decimal to ASCII. We'll sum the longs and write a custom helper function to decode the message:

Long productNumbers = products.stream().collect(Collectors.summingLong(Product::getProductNumber));
System.out.println(productNumbers);
System.out.println(decodeMessage(productNumbers));

The decodeMessage() method takes in a Long, breaks it up and considers each two characters as a decimal representation of a Character, before adding the characters up and returning the message:

public static String decodeMessage(Long encodedMessage) {
    String message = String.valueOf(encodedMessage);
    String[] characters = message.split("");
    StringBuilder result = new StringBuilder();

    for (int i = 1; i < characters.length; i+=2) {
        result.append(Character.toString(Integer.parseInt(characters[i-1]+characters[i])));
    }

    return result.toString();
}

Awesome, let's take a look at the result:

74658665
JAVA

Not an exciting secret message, by all means, but a message nonetheless.

Conclusion

In this guide, we've taken a look at summing collectors, and explored the summingInt(), summingDouble() and summingLong() methods.

We've explored their usage on primitive wrappers, as well as custom objects, which are typically reduced to a field for summation operations.

Last Updated: November 28th, 2021
Was this article helpful?

© 2013-2024 Stack Abuse. All rights reserved.

AboutDisclosurePrivacyTerms