Java Stream API: Exploring Collectors.teeing() collector

Eric Anicet
3 min read3 days ago

--

The Collector.teeing feature introduced in Java 12 is a powerful addition to the Collector interface in the Java Stream API.

The teeing() method

teeing() is a static method of the Collectors class that is used to return a Collector combining the results of two Collector operations. Every element passed to the resulting collector is processed by both downstream collectors, then their results are merged using the specified merge function into the final result.

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
)

Explanation

The Collector.teeing method takes three arguments:

  • Collector<? super T, ?, R1> downstream1: The first downstream collector that collects elements from the stream.
  • Collector<? super T, ?, R2> downstream2: The second downstream collector also collects elements from the stream.
  • BiFunction<? super R1,? super R2, R> merger: The merging function to combine the results of the first and the second collector.

The resulting collector functions do the following:

  • supplier: creates a result container that contains result containers obtained by calling each collector’s supplier
  • accumulator: calls each collector’s accumulator with its result container and the input element
  • combiner: calls each collector’s combiner with two result containers
  • finisher: calls each collector’s finisher with its result container, then calls the supplied merger and returns its result.

The resulting collector is Collector.Characteristics.UNORDERED if both downstream collectors are unordered and Collector.Characteristics.CONCURRENT if both are concurrent.

Sample Code

Let’s consider a scenario where we have a list of Person objects.

record Person(String name, String city, int age){}

Example 1: Find the minimum and maximum age from the list of Person objects

public static void main(String[] args) {

List<Person> people = List.of(new Person("Alex", "Paris", 32),
new Person("Martin", "Paris", 24),
new Person("Tim", "Paris", 23),
new Person("Emilie", "Berlin", 25),
new Person("Albert", "Chicago", 24),
new Person("Mateo", "Madrid", 25),
new Person("Adrien", "Barcelone", 27));

var minMaxAge = people.stream().collect(
Collectors.teeing(
Collectors.minBy(Comparator.comparingInt(Person::age)),
Collectors.maxBy(Comparator.comparingInt(Person::age)),
(min, max) -> "Min = " + min.get() + ", Max = " + max.get() // Merge
)
);
System.out.println(minMaxAge);
}
Prints:

Min = Person[name=Tim, city=Paris, age=23], Max = Person[name=Alex, city=Paris, age=32]

Example 1: Find the list of persons living in Paris
and the list of persons 24 years of age

 public static void main(String[] args) {

List<Person> people = List.of(new Person("Alex", "Paris", 32),
new Person("Martin", "Paris", 24),
new Person("Tim", "Paris", 23),
new Person("Emilie", "Berlin", 25),
new Person("Albert", "Chicago", 24),
new Person("Mateo", "Madrid", 25),
new Person("Adrien", "Barcelone", 27));

List<List<Person>> result = people.stream()
.collect(Collectors.teeing(
Collectors.filtering(p -> p.city().equals("Paris"),
Collectors.toList()),
Collectors.filtering(p -> 24 == p.age(),
Collectors.toList()),
List::of
));

System.out.println(result);
}
Prints:
[[Person[name=Alex, city=Paris, age=32],
Person[name=Martin, city=Paris, age=24],
Person[name=Tim, city=Paris, age=23]],

[Person[name=Martin, city=Paris, age=24],
Person[name=Albert, city=Chicago, age=24]]]

Example 1: Count how many live in Paris and how many persons are emitted by the stream

public static void main(String[] args) {

List<Person> people = List.of(new Person("Alex", "Paris", 32),
new Person("Martin", "Paris", 24),
new Person("Tim", "Paris", 23),
new Person("Emilie", "Berlin", 25),
new Person("Albert", "Chicago", 24),
new Person("Mateo", "Madrid", 25),
new Person("Adrien", "Barcelone", 27));

List<Long> result = people.stream()
.collect(Collectors.teeing(
Collectors.filtering(p -> p.city().equals("Paris"),
Collectors.counting()),
Collectors.counting(),
List::of)
);

System.out.println(result);
}
Prints:
[3, 7]

Conclusion

The Collectors.teeing() method is a powerful tool for performing multiple stream operations in a single pass, making your code more efficient and expressive.

If you loved reading the story, don’t forget to clap 👏. You can reach out to me and follow me on Medium, Twitter, GitHub, Linkedln

Support me through GitHub Sponsors.

Thank you for Reading !! See you in the next story.

References

--

--

No responses yet