JB Header
Java 8 - How to use Collectors.collectingAndThen Collector with examples
This tutorial explains how to use the predefined collector returned by Collectors.collectingAndThen() method with examples. It first explains the method definition and then shows collectingAndThen() method's usage using two Java 8 code examples, along with detailed explanation of the code. Definition of Collectors.collectingAndThen() method Collectors.collectingAndThen() method is defined with the following signature -
public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher)
Where,
     - 1st input parameter is downstream which is an instance of a Collector<T,A,R> i.e. the standard definitionClick to Read Tutorial explaining Basics of Java 8 Collectors of a collector. In other words, any collector can be used here.
     - 2nd input parameter is finisher which needs to be an instance of a FunctionClick to Read Tutorial on Function functional interfaces<R,RR> functional interface. This function instance takes as input an object of type R which is the output from downstream collector, and it returns an output of type RR which is the final return type of collectingAndThen collector as well.
     - output is a Collector with finisherClick to Read tutorial on 4 components of Collectors incl. 'finisher'(return type) of type RR. How the Collectors.collectingAndThen() method works CollectingAndThen() method first collects the elements of type T of Stream<T> using the Collector<T,A,R> passed to it as the first parameter. As a result of applying the collector, stream elements are collected into an object of type R. Using the Function<R,RR> instance passed as the second parameter, the collected object of type R is then transformed to an object of type RR. This object of type RR is the final object/value returned by the collectingAndThen collector.
Thus, when there is a scenario where the stream elements need to be collected and then the collected object needs to be transformed using a given rule\function, then using the collectingAndThen collector both these tasks of collection and transformation can be specified and executed together. Example#1 of Collectors.collectingAndThen Problem Description: Given a stream of employees, we want to -
  1. Find the employee with the maximum salary for which we want to use the maxBy collector.
  2. The output of the maxBy collector being an Optional value, we want to check whether a value is present and then print the max salaried employee's name.
Solution code for the above problem is -
Java 8 Code Example#1 for Collectors.collectingAndThen
package com.javabrahman.java8.collector;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import com.javabrahman.java8.Employee;

public class CollectingAndThenExample {
  static List<Employee> employeeList
      = Arrays.asList(new Employee("Tom Jones", 45, 15000.00),
      new Employee("Tom Jones", 45, 7000.00),
      new Employee("Ethan Hardy", 65, 8000.00),
      new Employee("Nancy Smith", 22, 10000.00),
      new Employee("Deborah Sprightly", 29, 9000.00));

  public static void main(String[] args) {
    String maxSalaryEmp = employeeList.stream().collect(
        Collectors.collectingAndThen(
            Collectors.maxBy(Comparator.comparing(Employee::getSalary)),
            (Optional<Employee> emp)-> emp.isPresent() ? emp.get().getName() : "none") );
    System.out.println("Max salaried employee's name: "+ maxSalaryEmp);
  }
}

//Employee.java POJO class
package com.javabrahman.java8;
import java.text.DecimalFormat;
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;
  }

  //Standard setters and getters for name,age and salary go here
 
  public String toString(){
    DecimalFormat dformat = new DecimalFormat(".##");
    return "Employee Name:"+this.name;
  }

  //Standard hashcode() & equals() implementations go here
}
 OUTPUT of the above code
Max salaried employee's name: Tom Jones
Explanation of the code
Example#2 of Collectors.collectingAndThen Problem Description: Given a stream of employees, we want to -
  1. Find the average salary of all employees using averagingDouble collector.
  2. Print the average salary after formatting it using DecimalFormat.
Solution code for the above problem is -
(Note - The Employee class and employeeList objects with their values remain the same as the previous code usage example and hence are not shown below for brevity.)
Java 8 Code Example#2 - Collectors.collectingAndThen
  public static void main(String[] args) {
    System.out.println("Max salaried employee's name: " + maxSalaryEmp);
    String avgSalary = employeeList.stream().collect(
        Collectors.collectingAndThen(
            Collectors.averagingDouble(Employee::getSalary),
            averageSalary -> new DecimalFormat("'$'0.00").format(averageSalary)));
    System.out.println("Average salary in $: " + avgSalary);
  }
 OUTPUT of the above code
Average salary in $: $9800.00
Explanation of the code
  • collectingAndThen collector takes 2 parameters -
    1. Collectors.averagingDouble collector with the Employee’s salary passed as the attribute to be averaged using method reference - “Employee::getSalary”.
    2. Function<Double,String> instance specified using a lambda expression -“averageSalary -> new DecimalFormat("'$'0.00").format(averageSalary))” which specifies that the average salary returned by the averagingDouble collector is to be formatted using DecimalFormat with specified format - “'$'0.00”.
  • Output printed is as expected - properly formatted average salary of all employees - “$9800.00” is printed.