Mastering Collectors.teeing()
in Java Streams (With Examples)
Java 12 introduced Collectors.teeing()
, a powerful feature in the Stream API, allowing us to combine two separate collectors and merge their results into a single output.
Instead of running two separate stream operations, teeing()
processes the data in one pass, improving performance and readability.
🛠️ Understanding Collectors.teeing()
🔹 Syntax
Collectors.teeing(
Collector<T, A, R1> downstream1, // First collector
Collector<T, A, R2> downstream2, // Second collector
BiFunction<R1, R2, R> merger // Merging function
)
downstream1
→ First collector operation.downstream2
→ Second collector operation.merger
→ Combines both collected results into the final output.
💡 Why Use Collectors.teeing()
?
✅ Avoids multiple stream traversals (better performance).
✅ Use it for summarizing, grouping, or partitioning data in a single .collect()
operation.
✅ Combines different results in a single operation.
✅ Improves readability compared to separate stream calls.
Happy learning :)
📌 Example 1: Calculate Sum & Average Together
Problem: Given a list of numbers, calculate both sum and average in one pass using teeing()
.
import java.util.List;
import java.util.stream.Collectors;
public class TeeingExample {
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(Integer::intValue), // Collector 1: Sum
Collectors.averagingDouble(Integer::doubleValue), // Collector 2: Average
(sum, avg) -> "Sum = " + sum + ", Average = " + avg // Merge results
));
System.out.println(result);
}
}
//-------OUTPUT---//
Sum = 150, Average = 30.0
✨ Key Benefit: This avoids separate .sum()
and .average()
calculations, making the code more efficient.
📌 Example 2: Find Min & Max Together
Problem: Given a list of numbers, find both minimum and maximum values in a single operation.
import java.util.List;
import java.util.stream.Collectors;
public class TeeingMinMax {
public static void main(String[] args) {
List<Integer> numbers = List.of(5, 10, 2, 8, 20, 3);
var minMax = numbers.stream()
.collect(Collectors.teeing(
Collectors.minBy(Integer::compareTo), // Collector 1: Min
Collectors.maxBy(Integer::compareTo), // Collector 2: Max
(min, max) -> "Min = " + min.get() + ", Max = " + max.get() // Merge
));
System.out.println(minMax);
}
}
//---OUTPUT---//
Min = 2, Max = 20
✨ Key Benefit: Instead of using .min()
and .max()
separately, teeing()
combines them efficiently in one pass.
📌 Example 3: Count Even & Odd Numbers in a List
Problem: Count how many numbers are even and odd in a list.
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class TeeingEvenOdd {
public static void main(String[] args) {
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Map<String, Long> result = numbers.stream()
.collect(Collectors.teeing(
Collectors.filtering(n -> n % 2 == 0, Collectors.counting()), // Count evens
Collectors.filtering(n -> n % 2 != 0, Collectors.counting()), // Count odds
(evenCount, oddCount) -> Map.of("Even Count", evenCount, "Odd Count", oddCount)
));
System.out.println(result);
}
}
//----OUTPUT--//
{Even Count=5, Odd Count=5}
✨ Key Benefit: Instead of two separate filter operations, we use teeing()
to count both even and odd numbers in one pass.
🚀 Performance Benefits of Collectors.teeing()
🔹 Without teeing()
(Multiple Stream Traversals)
long evenCount = numbers.stream().filter(n -> n % 2 == 0).count();
long oddCount = numbers.stream().filter(n -> n % 2 != 0).count();
🔴 Two separate .stream()
calls → Extra computation cost.
🔹 With teeing()
(Single Stream Traversal)
numbers.stream().collect(Collectors.teeing(...));
✅ Only one .stream()
call → Faster execution.
📌 When to Use Collectors.teeing()
?
📌 Limitations of Collectors.teeing()
❌ Available only in Java 12+ (not in Java 8 or 11).
❌ Only works with two collectors (not three or more).
❌ Less useful if both collectors need post-processing.