
Java 12 introduced the Collectors.teeing()
method, which allows a single stream to be processed by two different collectors simultaneously, and then combines their results into a final output.
1. What is Collectors.teeing()
?
✅ teeing()
runs two collectors at the same time on a stream.
✅ The results from both collectors are combined using a third function.
✅ Useful for performing multiple aggregations in a single pass over the data.
2. Syntax of Collectors.teeing()
Collectors.teeing(
Collector<T, ?, R1> collector1, // First collector
Collector<T, ?, R2> collector2, // Second collector
BiFunction<R1, R2, R> merger // Function to combine results
)
✔ Processes elements once, reducing computational overhead.
3. Example: Finding the Average and Sum in One Pass
📌 Problem: Given a list of numbers, calculate:
- The sum of all numbers.
- The average of all numbers.
- Return both values as a
String
.
🔹 Java 12+ Solution Using teeing()
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = List.of(10, 20, 30, 40, 50);
String result = numbers.stream()
.collect(Collectors.teeing(
Collectors.summingInt(n -> n), // Collector 1: Sum
Collectors.averagingInt(n -> n), // Collector 2: Average
(sum, avg) -> "Sum: " + sum + ", Average: " + avg // Merge results
));
System.out.println(result);
}
}
✅ Output:
Sum: 150, Average: 30.0
✔ No need to process the list twice!
4. When Should You Use teeing()
?
🚀 Use teeing()
when:
- You need to compute two independent results from a single stream.
- You want to combine those results into a single output.
- You want to improve performance by avoiding multiple iterations.
📌 Example: Finding the Min and Max in One Pass
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = List.of(5, 2, 8, 1, 9);
String result = numbers.stream()
.collect(Collectors.teeing(
Collectors.minBy(Integer::compare), // Find Min
Collectors.maxBy(Integer::compare), // Find Max
(min, max) -> "Min: " + min.get() + ", Max: " + max.get() // Combine
));
System.out.println(result);
}
}
✅ Output:
Min: 1, Max: 9
✔ Faster than processing the list twice!
5. Comparison: Before Java 12 vs. Java 12+ (teeing()
)
Feature | Before Java 12 (Multiple Passes) | Java 12+ (teeing() ) |
---|---|---|
Computing sum and average | Two separate .collect() calls | Single .collect(teeing()) call |
Finding min and max | Two separate .min() and .max() calls | Single .collect(teeing()) call |
Performance | Iterates the stream twice | Processes the stream once |
Lesson Reflection
- Why is
Collectors.teeing()
better than calling two separatecollect()
methods? - Can you think of another use case where
teeing()
would be helpful? - What happens if one of the collectors in
teeing()
returns an empty result?
🔹 What If You Need More Than Two Collectors?
If you need more than two collectors, you have to nest teeing()
calls or use collect(Collectors.toMap())
instead.
📌 Example: Using Nested teeing()
for Three Collectors
🚀 Problem: Find sum, average, and count in a single stream pass.
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = List.of(10, 20, 30, 40, 50);
var result = numbers.stream()
.collect(Collectors.teeing(
Collectors.summingInt(n -> n), // Sum
Collectors.teeing(
Collectors.averagingInt(n -> n), // Average
Collectors.counting(), // Count
(avg, count) -> "Average: " + avg + ", Count: " + count
),
(sum, avgCount) -> "Sum: " + sum + ", " + avgCount
));
System.out.println(result);
}
}
✅ Output:
Sum: 150, Average: 30.0, Count: 5
✔ We nested one teeing()
inside another to use three collectors.
🔹 Alternative: Using collect(Collectors.toMap())
If you need more than two aggregations, another option is using a Map
:
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = List.of(10, 20, 30, 40, 50);
Map<String, Object> result = numbers.stream()
.collect(Collectors.toMap(
n -> "Result",
n -> Map.of(
"Sum", numbers.stream().mapToInt(i -> i).sum(),
"Average", numbers.stream().mapToInt(i -> i).average().orElse(0),
"Count", numbers.size()
),
(a, b) -> b // Merge function (keeps last value)
));
System.out.println(result.get("Result"));
}
}
✅ Output:
{Sum=150, Average=30.0, Count=5}
✔ toMap()
is a flexible alternative when you need multiple aggregations.
🔍 Key Takeaways
✔ teeing()
only supports two collectors.
✔ For more than two collectors, nest teeing()
or use toMap()
.
✔ Use teeing()
when you need just two aggregations; use toMap()
for more flexibility.
The next Java 13+ feature: Text Blocks ("""
Multiline Strings)? 😊🚀