This tutorial shows you how to use Collectors.teeing
which works in Java 12 or above.
You may have been familiar with Collector
in Java. Collector
is usually used to process each element into an accumulated result. There are some static methods for creating Collector
such as toMap
, toList
, and groupingBy
. What if you need to process each element using more than one downstream collectors.
Since Java 12, there is a static method of Collector
that passes each element to two downstream collectors, then merges the results of both collectors using a specified merge function. You can use Collectors.teeing
for that purpose. Below are the examples.
Using Collectors.teeing
Here's the method to be used.
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
)
For using Collectors.teeing
, you need to pass two collectors as the first and second arguments. Each element will be processed by both collectors. For merging the results, you need to pass a BiFunction
as the third argument which acts as a merge function. Inside the merge function, you can use the values from both collectors, then return any type of output.
Below are some usage examples of Collectors.teeing
.
Getting Average of Numbers
In the first example, we are going to get the average value of numbers. The first downstream collectors summingDouble
is used to summarize all values. The second downstream collector counting
is for counting the number of elements. The merge function returns the average by dividing the value from the first downstream (sum) by the value from the second downstream (count).
double average = Stream.of(1, 2, 3, 4, 5, 6)
.collect(Collectors.teeing(
Collectors.summingDouble(i -> i),
Collectors.counting(),
(sum, count) -> sum / count
));
System.out.println(average);
Output:
3.5
Getting Minimum and Maximum Elements
For the second example, we use a class named Item
and a List
containing some instances of Item
. Using Collectors.teeing
, we are going to find the cheapest and the most expensive items. The first downstream collector minBy
is used to get the minimum value, while the second downstream collector is used to get the maximum value.
@AllArgsConstructor
@Getter
@Setter
public static class Item {
private int id;
private String name;
private int price;
}
List<Item> ItemList = List.of(
new Item(1, "One", 1000),
new Item(2, "Two", 2500),
new Item(3, "Three", 500),
new Item(4, "Four", 1200)
);
Map<String, Item> result = ItemList.stream()
.collect(
Collectors.teeing(
Collectors.minBy(Comparator.comparing(Item::getPrice)),
Collectors.maxBy(Comparator.comparing(Item::getPrice)),
(e1, e2) -> Map.ofEntries(
Map.entry("min", e1.get()),
Map.entry("max", e2.get())
)
)
);
System.out.println("Cheapest item: " + result.get("min").getName());
System.out.println("Most expensive item: " + result.get("max").getName());
Output:
Cheapest item: Three
Most expensive item: Two
Filter and Count Matching Elements
For the third example, we are going to use the same list for filtering items whose price is greater than 1000 and counts the number of matching elements at the same time. We need to use Collectors.filtering
for both downstreams. For the first downstream, we pass a Predicate
as the first argument and Collectors.toList
as the second argument. For the first downstream, we pass the same Predicate
as the first argument and Collectors.counting
as the second argument.
Predicate<Item> predicate = item -> item.getPrice() > 1000;
Map<String, Object> result = ItemList.stream()
.collect(
Collectors.teeing(
Collectors.filtering(predicate, Collectors.toList()),
Collectors.filtering(predicate, Collectors.counting()),
(items, count) -> Map.ofEntries(
Map.entry("items", items),
Map.entry("count", count)
)
)
);
System.out.println("Number of matching items: " + result.get("count"));
System.out.println("Matching items:");
List<Item> items = (List<Item>) result.get("items");
for (Item item : items) {
System.out.println(item.getName());
}
}
Output:
Number of matching items: 2
Matching items:
Two
Four
That's how to use Collectors.teeing
. Make sure you use at least JDK 12 or above for using it.