JB Header
Java 8 Reducing with Streams | reduce method tutorial with examples
Introduction Java 8 Reducing with Streams tutorial starts with explaining the concept of reducing in Streams. It then looks at the Streams API's reduce() method and how it can be used to perform reduction operations on streams of data. Java 8 code examples are used to demonstrate the method's usage. This tutorial assumes that the reader is familiar with basics of Java 8 Streams APIRead Basics of Java 8 Streams API. What is 'reducing' in the context of Streams Reducing in the context of Java 8 Streams refers to the process of combining all elements in the stream repeatedly to produce a single value which is returned as the result of the reduction operation.

Given a stream of elements there could be various ways in which one can reduce (or combine) them to a single resultant value such as summation of all elements (for numeric types), finding the maximum element from among all the elements (based on the elements' comparison order), and similar operations for combining multiple elements into a single resultant value.

The primary requirement of any reduction operation's logic is that it should use two operands for the operation which are -
  1. The collective value aggregated or derived from the elements encountered so far which will be of the same type as the type of elements in the stream.
  2. The value which is encountered next as the unprocessed value in the stream.
Due to this inherent nature of reduction operations requiring two operands both of which are of the same type as the type of elements in the stream being processed, Stream API's reduce() method also uses a BinaryOperator function for defining the reduction operation logic.

Let us now take a look at how the Stream API's reduce() operation is defined and used.
Stream.reduce() method Stream.reduce() method has the following signature -
T reduce(T identity, BinaryOperator<T> accumulator);
Where,
     - identity is initial value of type T which will be used as the first value in the reduction operation.
     - accumulator is an instance of a BinaryOperator Function(Functional Interface) of type T.

How the stream.reduce() operation works is that it applies the BinaryOperator function's logic to the elements of the stream repeatedly. BinaryOperator operates on 2 operands of type T where -
  1. The first operand contains the reduced (or combined) value till the current processed value in the stream. When the reducing operation starts, the identity value supplied to the reduce() method is used as this operand's value.
  2. The second operand is the next unprocessed value obtained from the Stream.
  3. (Note that 1 & 2 exactly match reduction operation's operand definition described above)
Stream.reduce() is a terminal operationClick to Read Tutorial explaining intermediate & terminal Stream operations.

Let us now move to the 1st example where the reduction operation sums up all elements of the stream. After the code I will explain in detail how the summation logic works using the Stream.reduce() method. Example 1: Finding aggregate of stream elements using Stream.reduce() method
Example 1-Java 8 code showing Stream.reduce() method for aggregation
//Employee.java
package com.javabrahman.java8;
public class Employee{
  private String name;
  private Integer age;
  private Double salary;
  public Employee(String name, Integer age, Double salary){
    this.name=name;
    this.age=age;
    this.salary=salary
  } 
  //getters and setters for name and age attributes go here
  //overridden equals() and hashcode() go here
  public String toString() {
    return "Employee Name: "+this.name
      +"  Age: "+this.age
      +"  Salary: "+this.salary;
  }
}
//ReducingWithStreams.java
package com.javabrahman.java8.streams;
import com.javabrahman.java8.Employee;
import java.util.Arrays;
import java.util.List;
public class ReducingWithStreams {
  static List<Employee> employeeList = Arrays.asList(
      new Employee("Tom Jones", 45, 7000.00),
      new Employee("Harry Major", 25, 10000.00),
      new Employee("Ethan Hardy", 65, 8000.00),
      new Employee("Nancy Smith", 22, 12000.00),
      new Employee("Deborah Sprightly", 29, 9000.00));

  public static void main(String[] args) {
    Double totalSalaryExpense = employeeList.stream()
                               .map(emp -> emp.getSalary())
                               .reduce(0.00,(a,b) -> a+b);
    System.out.println("Total salary expense: "+totalSalaryExpense);
   }
}
 OUTPUT of the above code
Total salary expense:  46000.0
Explanation of the code
  • Employee is the class of which we will be creating a Stream. It has two main attributes - name and age.
  • employeeList is a static list of 5 Employees.
  • Our aim is to calculate the total salary expenses of the company for which we will find the sum of all employee's salaries using Stream.reduce() method.
  • We start off with creating a Stream of Employees from employeeList using the stream() method of List interface.
  • We then map the StreamClick to read how Mapping with Java8 Streams works of Employee objects into a Stream of salaries of employees of type Double.
  • Next we use the reduce() method, pipelined to the Stream of Double-valued salaries, to start aggregating the salaries. We pass the lambda expressionClick to read tutorial on Java 8 Lambda Expressions - (a,b) -> a+b for specifying the BinaryOperator function's logic.
  • This is how the reduction operation (of aggregation) will work as the Double-valued salaries are encountered by the reduce() method -
    • 1stcall to the BinaryOperator function - Sum of 0.00(initial value) + 7000.00(1st value from stream)
    • 2ndcall - 7000.00(sum till now) + 10000.00(2nd value from stream)
    • 3rdcall - 17000.00(sum till now) + 8000.00(3rd value from stream)
    • 4thcall - 25000.00(sum till now) + 12000.00(4th value from stream)
    • 5thcall - 37000.00(sum till now) + 9000.00(5th value from stream)
  • The total salary expense of 46000.00 is returned as the resultant value of stream() operation and assigned to the totalSalaryExpense variable which is then printed as output.
Let us now take a look at the 2nd example showing the reduce() method usage.This time we will find the employee with the maximum salary from among all employees.
(Note - The Employee class and static employeeList are same as the above example and hence have been left out of the code below for brevity)
Example 2: Using Stream.reduce() method for finding employee with maximum salary
Example 2-Stream.reduce()method for finding employee with maximum salary
public static void main(String[] args) {
Optional<Employee> maxSalaryEmp=employeeList.stream()
    .reduce((Employee a, Employee b) -> a.getSalary() < b.getSalary() ? b:a);
if(maxSalaryEmp.isPresent())
  System.out.println("Employee with max salary: "+maxSalaryEmp.get());
}
 OUTPUT of the above code
Employee with max salary:    Employee Name: Nancy Smith    Age:22    Salary: 12000.
Explanation of the code
  • Our aim is to find the Employee object with the maximum value stored in the salary attribute using Stream.reduce() method.
  • We start off with creating a Stream of Employees from employeeList using the stream() method of List interface.
  • Next we use the reduce() method, pipelined to the Stream of Employeeobjects, to start with the process of finding the employee with the maximum salary. We pass the lambda expression- (Employee a, Employee b) -> a.getSalary()< b.getSalary() ? b : a   for specifying the BinaryOperator function's logic.
  • This example uses the overloaded variant of reduce() method which doesn't take the initial value and returns an Optional<Employee> type of value containing the result.
  • This is how the reduction operation (of finding maximum salary) will work as the salaries are encountered by the reduce() method -
    • 1stcall to the BinaryOperator function - returns maximum of 7000.00(1st value from stream) and 10000.00(2nd value from stream)
    • 2ndcall - returns max of 10000.00(max value till now) and 8000(3rd value from stream)
    • 3rdcall - returns max of 10000.00(max value till now) and 12000.00(4th value from stream)
    • 4thcall - returns max of 12000.00(max value till now) and 9000.00(5th value from stream)
  • 12000.00 is the maximum value and the Employee object with this salary is returned inside an Optional<Employee> instance and assigned to maxSalaryEmp variable.
  • We check for presence of value inside maxSalaryEmp using Optional’s isPresent() method and then print the Employee object obtained using Optional.get() method.
Conclusion In this tutorial we first learnt what is meant by reducing in the context of Java 8 Streams. Next we looked at the reduce() method provided by Streams API for the purpose of performing reduction operations. Lastly, we understood the working of the reduce() method by looking at code examples for two different stream reduction operations of aggregation and finding the maximum value.