The Stream.reduce() Method: Aggregating Data

Published: September 12, 2016


Introduction

In Java 8, the reduce() method is part of the Streams API and provides a powerful way to aggregate data. With this method, you can accumulate elements of a stream into a single result, such as summing a list of numbers, finding the maximum or minimum, or combining strings.

In this tutorial, we’ll explore:

  • What the reduce() method does.
  • How to use the reduce() method to perform aggregation.
  • Practical examples of using reduce() in different scenarios.

By the end of this tutorial, you’ll understand how to use the reduce() method to simplify your code and perform data aggregation with ease.


What is the reduce() Method?

The reduce() method performs a reduction on the elements of a stream using an associative accumulation function. It combines the elements of the stream in a sequential or parallel manner.

The signature of the reduce() method looks like this:

Optional<T> reduce(BinaryOperator<T> accumulator);

It takes a BinaryOperator, which is a functional interface that represents an operation on two operands of the same type. The reduce() method applies this accumulator function to combine the elements of the stream.

It can also take an identity value, which is a starting value used in the accumulation process:

T reduce(T identity, BinaryOperator<T> accumulator);

Simple Example: Summing Numbers

Let’s start with a simple example of using reduce() to sum a list of integers:

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

public class ReduceExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // Summing numbers using reduce()
        int sum = numbers.stream()
                         .reduce(0, (a, b) -> a + b); // Identity value is 0

        System.out.println("Sum: " + sum); // Output: Sum: 15
    }
}

In this example:

  • The identity value is 0.
  • The accumulator function (a, b) -> a + b adds two integers together.
  • The result is the sum of the numbers in the list.

Using reduce() Without an Identity Value

If you don’t provide an identity value, reduce() will return an Optional<T> because there may be no elements in the stream (in which case the result is empty).

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

public class ReduceExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // Summing numbers without an identity value
        Optional<Integer> sum = numbers.stream()
                                       .reduce((a, b) -> a + b);

        sum.ifPresent(System.out::println); // Output: 15
    }
}

In this example, if the list is empty, the result will be an empty Optional, which is why we use ifPresent() to print the sum only if it’s present.


Practical Examples

Example 1: Finding the Maximum Value

You can use reduce() to find the maximum value in a list:

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

public class ReduceExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6, 5, 3);

        // Finding the maximum value using reduce()
        int max = numbers.stream()
                         .reduce(Integer.MIN_VALUE, (a, b) -> a > b ? a : b);

        System.out.println("Maximum value: " + max); // Output: Maximum value: 9
    }
}

Here, we use Integer.MIN_VALUE as the identity value and apply the max logic in the accumulator function. This returns the maximum value in the list.


Example 2: Concatenating Strings

You can also use reduce() to concatenate strings:

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

public class ReduceExample {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("Java", "is", "fun");

        // Concatenating strings using reduce()
        String result = words.stream()
                             .reduce("", (a, b) -> a + " " + b); // Identity value is ""

        System.out.println("Concatenated string: " + result); // Output: Concatenated string: Java is fun
    }
}

In this case, the identity value is an empty string (""), and the accumulator function concatenates the strings with a space in between.


Using reduce() for Complex Aggregations

For more complex aggregations, such as calculating the average of a list of numbers, you can combine reduce() with other methods like map() or collect().

Example 3: Calculating Average

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

public class ReduceExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // Calculating average using reduce()
        double average = numbers.stream()
                                .mapToInt(Integer::intValue)
                                .reduce(0, (a, b) -> a + b) / (double) numbers.size();

        System.out.println("Average: " + average); // Output: Average: 3.0
    }
}

In this example, we first map the stream to integers, then use reduce() to sum the values and divide by the size of the list to calculate the average.


Advantages of reduce()

  • Concise and Expressive: The reduce() method allows you to express complex data transformations in a functional and declarative manner.
  • Parallelizable: Like other stream operations, reduce() can be used in parallel streams to take advantage of multi-core processors, making it suitable for large datasets.

Conclusion

The reduce() method is a versatile tool for aggregating data in streams. Whether you’re summing values, concatenating strings, or finding the maximum, reduce() can simplify your code and make your data-processing tasks more elegant.

In the next tutorial, we’ll dive into Nashorn, a JavaScript engine in Java, and explore how to execute JavaScript from Java.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *