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
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 -
Let us now take a look at how the Stream API's
Where,
-
-
How the
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
OUTPUT of the above code
Explanation of the code
(Note - The
Example 2: Using
OUTPUT of the above code
Explanation of the code
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 -
- 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.
- The value which is encountered next as the unprocessed value in the stream.
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);
-
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 - - 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.
- The second operand is the next unprocessed value obtained from the Stream. (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);
}
}
Total salary expense: 46000.0
Employee
is the class of which we will be creating aStream
. It has two main attributes -name
andage
.employeeList
is a static list of 5Employee
s.- 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
ofEmployee
s fromemployeeList
using thestream()
method ofList
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 typeDouble
. - Next we use the
reduce()
method, pipelined to theStream
ofDouble
-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 theBinaryOperator
function's logic. - This is how the reduction operation (of aggregation) will work as the
Double
-valued salaries are encountered by thereduce()
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)
- 1stcall to the
- The total salary expense of 46000.00 is returned as the resultant value of
stream()
operation and assigned to thetotalSalaryExpense
variable which is then printed as output.
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)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());
}
Employee with max salary: Employee Name: Nancy Smith Age:22 Salary: 12000.
- 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
ofEmployee
s fromemployeeList
using thestream()
method ofList
interface. - Next we use the
reduce()
method, pipelined to theStream
ofEmployee
objects, 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 theBinaryOperator
function's logic. - This example uses the overloaded variant of
reduce()
method which doesn't take the initial value and returns anOptional<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)
- 1stcall to the
- 12000.00 is the maximum value and the
Employee
object with this salary is returned inside anOptional<Employee>
instance and assigned tomaxSalaryEmp
variable. - We check for presence of value inside
maxSalaryEmp
usingOptional
’sisPresent()
method and then print theEmployee
object obtained usingOptional.get()
method.
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.Java 8 Streams API tutorials on JavaBrahman
Streams API - Introduction & BasicsClick to Read tutorial on Streams API Basics Understanding Stream Operations | Intermediate and Terminal OperationsClick to Read Tutorial on Stream Operations Overview Mapping with Streams using map and flatMap methodsClick to Read how Mapping with Java8 Streams works Filtering and Slicing with Streams using filter,distinct,limit,skip methodsClick to Read how Filtering and Slicing with Java8 Streams works Matching with Streams using allMatch,anyMatch,noneMatch methodsClick to Read tutorial on matching with Streams API Stream API's findFirst,findAny methods' tutorialClick to Read tutorial on findFirst() and findAny() methods of Streams API 'Peeking' into a running Stream with Stream.peek() methodClick to Read tutorial on Stream.peek() method Creating Infinite Streams with iterate\generate methodsClick to Read tutorial on Creating Infinite Streams Reducing Streams using Streams.reduce methodClick to Read tutorial on Reducing Streams
Streams API - Introduction & BasicsClick to Read tutorial on Streams API Basics Understanding Stream Operations | Intermediate and Terminal OperationsClick to Read Tutorial on Stream Operations Overview Mapping with Streams using map and flatMap methodsClick to Read how Mapping with Java8 Streams works Filtering and Slicing with Streams using filter,distinct,limit,skip methodsClick to Read how Filtering and Slicing with Java8 Streams works Matching with Streams using allMatch,anyMatch,noneMatch methodsClick to Read tutorial on matching with Streams API Stream API's findFirst,findAny methods' tutorialClick to Read tutorial on findFirst() and findAny() methods of Streams API 'Peeking' into a running Stream with Stream.peek() methodClick to Read tutorial on Stream.peek() method Creating Infinite Streams with iterate\generate methodsClick to Read tutorial on Creating Infinite Streams Reducing Streams using Streams.reduce methodClick to Read tutorial on Reducing Streams