Core Java

Java 8 Collect vs Reduce Example

Hello readers, this tutorial explains the concept of reducing and collecting in Streams and Collector’s API respectively.

1. Introduction

1.1 Stream’s reduce() Method

Let’s take a look at the Streams API’s reduce() method and how it can be used to perform reduction operations on the streams of data.
 
 
 
 

1.1.1 What is ‘reducing’ in the context of Streams?

Reducing in the context of Java8 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 result value such as summation of all elements (for numeric types), finding the maximum element from among all the elements (based on the element’s 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 to 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.

Method Signature

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

Where,

  • The identity is the initial value of the type T which will be used as the first value in the reduction operation
  • An accumulator is an instance of a BinaryOperator function of the type T

1.2 Stream’s collect() Method

Collectors play an important role in Java 8 streams processing. They ‘collect’ the processed elements of the stream into a final representation. Invoking the collect() method on a Stream, with a Collector instance passed as a parameter ends that Stream’s processing and returns back the final result. Stream.collect() method is thus a terminal operation. In other words, Stream.collect() method is used to receive the elements from a stream and store them in a collection.

The collect() operation accumulates the elements in a stream into a container such as a collection. It performs mutable reduction operation in which the reduced (final) value is a mutable result container such as an ArrayList. This method takes a Collector implementation that provides useful reduction operations. The Collectors class is a common implementation in the ‘JDK 1.8’. The following example accumulates emails of the persons into a list collection.

List<String> listEmails = listPersons.stream().map(p -> p.getEmail()).collect(Collectors.toList());
 
System.out.println("List of Emails: " + listEmails);

Now, open up the Eclipse Ide and let’s see how to work with Java8 Collectors and Streams API.

2. Java8 Collect vs. Reduce Example

2.1 Tools Used

We are using Eclipse Oxygen, JDK 8 and Maven.

2.2 Project Structure

Firstly, let’s review the final project structure, in case you are confused about where you should create the corresponding files or folder later!

Fig. 1: Application Project Structure
Fig. 1: Application Project Structure

2.3 Project Creation

This section will demonstrate on how to create a Java-based Maven project with Eclipse. In Eclipse IDE, go to File -> New -> Maven Project.

Fig. 2: Create Maven Project
Fig. 2: Create Maven Project

In the New Maven Project window, it will ask you to select project location. By default, ‘Use default workspace location’ will be selected. Select the ‘Create a simple project (skip archetype selection)’ checkbox and just click on next button to proceed.

Fig. 3: Project Details
Fig. 3: Project Details

It will ask you to ‘Enter the group and the artifact id for the project’. We will input the details as shown in the below image. The version number will be by default: 0.0.1-SNAPSHOT.

Fig. 4: Archetype Parameters
Fig. 4: Archetype Parameters

Click on Finish and the creation of a maven project is completed. If you observe, it has downloaded the maven dependencies and a pom.xml file will be created. It will have the following code:

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>Java8CollectReduce</groupId>
	<artifactId>Java8CollectReduce</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
</project>

Developers can start adding the dependencies that they want. Let’s start building the application!

3. Application Building

Below are the steps involved in developing this application.

3.1 Java Class Creation

Let’s create the required Java files. Right-click on the src/main/java folder, New -> Package.

Fig. 5: Java Package Creation
Fig. 5: Java Package Creation

A new pop window will open where we will enter the package name as: com.jcg.java.

Fig. 6: Java Package Name (com.jcg.java)
Fig. 6: Java Package Name (com.jcg.java)

Once the package is created in the application, we will need to create the ‘Collect’ and ‘Reduce’ implementation classes to illustrate the implementation of Collectors and Streams API in Java8. Right-click on the newly created package: New -> Class.

Fig. 7: Java Class Creation
Fig. 7: Java Class Creation

A new pop window will open and enter the file name as: ReduceTest. The implementation class will be created inside the package: com.jcg.java.

Fig. 8: Java Class (ReduceTest.java)
Fig. 8: Java Class (ReduceTest.java)

Repeat the step (i.e. Fig. 7) and enter the filename as: CollectTest. The implementation class will be created inside the package: com.jcg.java.

Fig. 9: Java Class (CollectTest.java)
Fig. 9: Java Class (CollectTest.java)

3.1.1 Implementation of Reduce Class

Let’s move to the 1st example where the reduction operation sums up all elements of the stream and the 2nd example where reduction operation finds the employee with the maximum salary. Let’s see the simple code snippet that follows this implementation.

ReduceTest.java

package com.jcg.java;

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

public class ReduceTest {

	static List<Employee> employeeList = Arrays.asList(
			new Employee("Tom", 45, 7000.00),
			new Employee("Harry", 25, 10000.00),
			new Employee("Ethan", 65, 8000.00),
			new Employee("Nancy", 22, 12000.00),
			new Employee("Deborah", 29, 9000.00));

	public static void main(String[] args) {

		/***** E.g. #1 - Total Salary Expenses *****/
		Double totalSalaryExpense = (Double) employeeList.stream().map(emp -> emp.getSalary()).reduce(0.00,(a,b) -> a+b);
		System.out.println("Total Salary Expense?= " + totalSalaryExpense + "\n");

		/***** E.g. #2 - Employee Details Having Maximum Salary *****/
		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());
		}
	}
}

3.1.2 Implementation of Collector Class

Let’s move to the example where the Stream.collect() method is used to receive elements from a stream and store them in a collection. Let’s see the simple code snippet that follows this implementation.

CollectTest.java

package com.jcg.java;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class CollectTest {

	public static void main(String[] args) {

		List<Employee> empList = new ArrayList<Employee>();
		empList.add(new Employee("Tom", 45, 7000.00));
		empList.add(new Employee("Harry", 25, 10500.00));
		empList.add(new Employee("Ethan", 65, 8000.00));
		empList.add(new Employee("Nancy", 22, 12000.00));
		empList.add(new Employee("Deborah", 29, 9000.00));

		/***** Find Employees Whose Salaries Are Above 10000 *****/
		List<Employee> filteredList = empList.stream().filter(emp->emp.getSalary() > 10000).collect(Collectors.toList());
		filteredList.forEach(System.out::println);
	}
}

Do remember, developers will have to use the ‘JDK 1.8‘ dependency for implementing this example.

4. Run the Application

To run the application, right-click on the ReduceTest or the CollectTest class, Run As -> Java Application. Developers can debug the example and see what happens after every step!

Fig. 10: Run Application
Fig. 10: Run Application

5. Project Demo

The application shows the following logs as output.

ConsumerTest.java

# Logs for 'ReduceTest' #
=========================
Total Salary Expense?= 46000.0

Employee with Max. Salary?= Employee Name: Nancy| Age: 22| Salary: 12000.0

# Logs for 'CollectTest' #
==========================
Employee Name: Harry| Age: 25| Salary: 10500.0
Employee Name: Nancy| Age: 22| Salary: 12000.0

That’s all for this post. Happy Learning!

6. Conclusion

In this tutorial, we looked at what are the collect() and reduce() methods defined in Java8. I hope this article served developers whatever they were looking for.

7. Download the Eclipse Project

This was an example of collect() and reduce() methods in Java8.

Download
You can download the full source code of this example here: Java8CollectReduce

Yatin

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button