Learnitweb

Java Teeing Collectors

1. Introduction

In Java 12, method teeing() was added in Collectors. This method returns a Collector that is a composite of two downstream collectors. It allows two different collectors on a stream and then merges their results using a specified BiFunction. The Collectors.teeing() method is useful when we want to simultaneously process a stream in two different ways and then combine their results.

Without the teeing() method, we’ll need to process the stream twice but with teeing we can process the result in single step.

2. Syntax

public static <T,​R1,​R2,​R> Collector<T,​?,​R> teeing​(Collector<? super T,​?,​R1> downstream1, Collector<? super T,​?,​R2> downstream2, BiFunction<? super R1,​? super R2,​R> merger)

This method returns a Collector which aggregates the results of two supplied collectors.

  • downstream1 – the first downstream collector
  • downstream2 – the second downstream collector
  • merger – the function which merges two results into the single one

Each element sent to the resulting collector is handled by both downstream collectors, and their outcomes are then combined using the specified merge function to produce the final result.

The resulting collector performs the following actions:

  • Supplier: Creates a result container that includes the result containers generated by each collector’s supplier.
  • Accumulator: Invokes each collector’s accumulator with its respective result container and the input element.
  • Combiner: Invokes each collector’s combiner with two result containers.
  • Finisher: Invokes each collector’s finisher with its result container, then calls the provided merger and returns the merged result.

3. Examples

3.1 Using Collectors.teeing() to Find Max and Min

In this example, we have few Employee objects. We can use teeing() to get the maximum and minimum salary.

import java.text.ParseException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

class Employee {
    int id;
    String name;
    int salary;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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 Employee(int id, String name, int salary) {
        this.id = id;
        this.name = name;
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", salary=" + salary +
                '}';
    }
}

public class TeeingExample {
    public static void main(String[] args) throws ParseException {
        List<Employee> employeeList = List.of(
                new Employee(1, "Emp1", 100),
                new Employee(2, "Emp1", 200),
                new Employee(3, "Emp1", 300),
                new Employee(4, "Emp1", 400));

        HashMap<String, Employee> result = employeeList.stream().collect(
                Collectors.teeing(
                        Collectors.maxBy(Comparator.comparing(Employee::getSalary)),
                        Collectors.minBy(Comparator.comparing(Employee::getSalary)),
                        (e1, e2) -> {
                            HashMap<String, Employee> map = new HashMap();
                            map.put("MAX", e1.get());
                            map.put("MIN", e2.get());
                            return map;
                        }
                ));

        System.out.println(result);
    }
}

Output

{MIN=Employee{id=1, name='A', salary=100}, MAX=Employee{id=4, name='D', salary=400}}

3.2 Using Collectors.teeing() to Average and Sum

In this example, we are calculating the sum and average of integers in a list.

public class TeeingExample {
    public static void main(String[] args) throws ParseException {
        List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5));

        String result = numbers.stream().collect(
                Collectors.teeing(
                        Collectors.averagingInt(Integer::intValue),
                        Collectors.summingInt(Integer::intValue),
                        (average, sum) -> String.format("Average: %.2f, Sum: %d", average, sum)
                )
        );

        System.out.println(result);
    }
}

Output

Average: 3.00, Sum: 15

4. Conclusion

In this tutorial, we’ve explored Java Teeing Collectors, a powerful feature introduced in Java 12 that allows you to process elements in a stream using multiple collectors and then merge their results.
Teeing Collectors enhance the flexibility and expressiveness of the Java Stream API, enabling you to perform complex data transformations and aggregations with ease. By leveraging Teeing Collectors, you can streamline your data processing pipelines, making your code more readable and maintainable.