April 30, 2024

Java Stream - Reduction Operations And Reduce Method

In this post we’ll see what are reduction operations in Java Stream API and how to use general-purpose reduction operation Stream.reduce() method.

Reduction operations in Java Stream

With in the Java Stream API there are many terminal operations (like average, sum, min, max, and count) that return one value by combining the contents of a stream. These operations are called reduction operations.

For example using the count reduction operation to count the number of elements in a List.

List<Integer> myList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);  
long count = myList.stream().count();
System.out.println("Count of elements in the list- " + count);
Output
Count of elements in the list- 10

Reduce methods in Java Stream

Java Stream API also has a general purpose reduce method to perform a reduction on the elements of the stream using the passed accumulator and returns a reduced value. Reduce method is overloaded and has 3 variants.

1. Optional<T> reduce(BinaryOperator<T> accumulator)- Performs a reduction on the elements of this stream, using an associative accumulation function and returns an Optional describing the reduced value, if any.

Accumulator is of type BinaryOperator which is a functional interface representing an operation upon two operands of the same type. The accumulator function takes two parameters: a partial result of the reduction and the next element of the stream.

Stream.reduce() with Accumulator example

Let’s say there is an Employee class with name, dept, salary fields. You need to find out the total salary using the Stream.reduce() method.

public class Employee {
  private String name;
  private String dept;
  private int salary;

  Employee(String name, String dept, int salary){
    this.name = name;
    this.dept = dept;
    this.salary = salary;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public int getSalary() {
    return salary;
  }
  public void setSalary(int salary) {
    this.salary = salary;
  }
  public String getDept() {
    return dept;
  }
  public void setDept(String dept) {
    this.dept = dept;
  }
}

Using reduce method by passing an accumulator function, which is a lambda expression in this example that adds two Integer values and returns an Integer value, you can get the total salary.

List<Employee> employeeList = new ArrayList<>();
employeeList.add(new Employee("Jack", "Finance", 5500));
employeeList.add(new Employee("Lisa", "Accounts", 5600));
employeeList.add(new Employee("Nikita", "IT", 4500));
employeeList.add(new Employee("Tony", "HR", 8000));
Optional<Integer> totalSalary = employeeList.stream().map(e -> e.getSalary()).reduce((a,b) -> a+b);
if(totalSalary.isPresent()){
  System.out.println("Total Salary- " + totalSalary.get());
}
Output
Total Salary- 23600

2. reduce(T identity, BinaryOperator<T> accumulator)- Performs a reduction on the elements of this stream, using the provided identity value and an associative accumulation function, and returns the reduced value.

  • identity- The identity element is both the initial value of the reduction and the default result if there are no elements in the stream.
  • accumulator- The accumulator function is an implementation of BinaryOperator which is a functional interface representing an operation upon two operands of the same type. The accumulator function takes two parameters: a partial result of the reduction and the next element of the stream

Stream.reduce() with Identity and Accumulator example

We can use the same example as above, only change is in the reduce method which now also passes an identity element as 0. This is the initial value of the sum of salaries and the default value if no members exist in the collection employeeList. Now the return type of the reduce method is also int.

List<Employee> employeeList = new ArrayList<>();
employeeList.add(new Employee("Jack", "Finance", 5500));
employeeList.add(new Employee("Lisa", "Accounts", 5600));
employeeList.add(new Employee("Nikita", "IT", 4500));
employeeList.add(new Employee("Tony", "HR", 8000));
int totalSalary = employeeList.stream().map(e -> e.getSalary()).reduce(0, (a,b) -> a+b);
System.out.println("Total Salary- " + totalSalary);

3. reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)- Performs a reduction on the elements of this stream, using the provided identity, accumulation and combining functions.

Combiner function is used for combining results of accumulator function, it must be compatible with the accumulator function. Combiner function is necessary when parallel stream is used in order to combine result of accumulators running in parallel.

Stream.reduce() with Identity, Accumulator and Combiner example

int value = Stream.of(1, 2, 3, 4, 5).parallel().reduce(1, (a, b) -> a*b, 
				(x,y) -> { System.out.println("In combiner function");
				           return x*y;});
System.out.println("Value- " + value);
Output
In combiner function
In combiner function
In combiner function
In combiner function
Value- 120

That's all for the topic Java Stream - Reduction Operations And Reduce Method. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment