# Mastering Java’s Collectors.teeing() with Practical Examples

### Introduction to `Collectors.teeing()`

Java 12 introduced the `Collectors.teeing()` method, which allows developers to process a stream in two separate ways simultaneously, then merge the results with a `BiFunction`. This powerful collector is especially useful when you need to aggregate data or calculate multiple values from a single stream pass.

### Syntax Overview

The `Collectors.teeing()` method takes three arguments:

1. **Collector1** - the first collector for partial data processing.
    
2. **Collector2** - the second collector for partial data processing.
    
3. **Merger** - a `BiFunction` that combines the results of both collectors into a final output.
    

Here's the basic syntax:

```java
Stream<T> stream = ...;
Collector<T, ?, R> result = stream.collect(Collectors.teeing(
    Collector1, 
    Collector2, 
    (result1, result2) -> mergeFunction(result1, result2)
));
```

### Practical Examples

#### Example 1: Calculating Average and Total Simultaneously

Let’s say you have a list of product prices, and you want to calculate both the average and the total price in a single operation.

```java
import java.util.List;
import java.util.stream.Collectors;

public class TeeingExample {
    public static void main(String[] args) {
        List<Double> prices = List.of(19.99, 9.99, 14.99, 29.99, 24.99);

        var result = prices.stream().collect(Collectors.teeing(
                Collectors.averagingDouble(Double::doubleValue), 
                Collectors.summingDouble(Double::doubleValue),
                (average, sum) -> "Average Price: " + average + ", Total Price: " + sum
        ));

        System.out.println(result); // Output: Average Price: 19.19, Total Price: 99.95
    }
}
```

In this example:

* **Collector1** computes the average price.
    
* **Collector2** calculates the sum of prices.
    
* **Merger** combines the average and total into a descriptive string.
    

#### Example 2: Finding the Longest and Shortest String in a List

For lists where you need the longest and shortest strings simultaneously, `Collectors.teeing()` can be handy.

```java
import java.util.List;
import java.util.Comparator;
import java.util.stream.Collectors;

public class TeeingStringExample {
    public static void main(String[] args) {
        List<String> words = List.of("stream", "collector", "Java", "teeing", "example");

        var result = words.stream().collect(Collectors.teeing(
                Collectors.maxBy(Comparator.comparingInt(String::length)),
                Collectors.minBy(Comparator.comparingInt(String::length)),
                (longest, shortest) -> "Longest: " + longest.orElse("N/A") + ", Shortest: " + shortest.orElse("N/A")
        ));

        System.out.println(result); // Output: Longest: collector, Shortest: Java
    }
}
```

Here:

* **Collector1** finds the longest string.
    
* **Collector2** finds the shortest string.
    
* **Merger** formats the output to display both results together.
    

#### Example 3: Counting and Grouping Items by Condition

Imagine you have a list of students with scores, and you want to count how many passed and failed based on a passing threshold.

```java
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

class Student {
    String name;
    int score;

    Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    public int getScore() {
        return score;
    }
}

public class TeeingCountExample {
    public static void main(String[] args) {
        List<Student> students = List.of(
            new Student("Alice", 75),
            new Student("Bob", 45),
            new Student("Charlie", 85),
            new Student("Daisy", 55)
        );

        Map<String, Long> result = students.stream().collect(Collectors.teeing(
                Collectors.filtering(s -> s.getScore() >= 60, Collectors.counting()),
                Collectors.filtering(s -> s.getScore() < 60, Collectors.counting()),
                (passed, failed) -> Map.of("Passed", passed, "Failed", failed)
        ));

        System.out.println(result); // Output: {Passed=2, Failed=2}
    }
}
```

Explanation:

* **Collector1** filters and counts students who passed (score &gt;= 60).
    
* **Collector2** filters and counts students who failed (score &lt; 60).
    
* **Merger** maps the counts to labels "Passed" and "Failed."
    

### When to Use `Collectors.teeing()`

`Collectors.teeing()` is useful when:

* You need multiple results from a single stream processing pass.
    
* You’re aggregating different calculations (e.g., count, sum, average) at once.
    
* You’re merging multiple conditions or classifications from a stream.
    

### Limitations and Considerations

1. **Java 12+ Required**: `Collectors.teeing()` is available only from Java 12 onwards.
    
2. **Performance**: While efficient for combined processing, complex operations may need careful testing for performance impacts on large data sets.
    
3. **Alternative Approaches**: For more than two collectors, consider multiple `.collect()` calls or switch to a `Map`.
    

### Conclusion

`Collectors.teeing()` is a powerful tool for combining results of two distinct collectors into one operation. It’s perfect for scenarios where you want to optimize performance by reducing the number of passes over the stream. By mastering collectors `teeing()`, you can make your Java code cleaner and more efficient when handling multiple results from the same data source.

**More such articles:**

[**https://medium.com/techwasti**](https://techwasti.com/Link)

[**https://www.youtube.com/@maheshwarligade**](https://www.youtube.com/@maheshwarligade)

[**https://techwasti.com/series/spring-boot-tutorials**](https://techwasti.com/series/spring-boot-tutorials)

[**https://techwasti.com/series/go-language**](https://techwasti.com/series/go-language)
