Java Stream API: Exploring Collectors.teeing() collector
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.