New in Java 12: The Teeing Collector

Original author: Marco Molteni
  • Transfer
In this article, we will look at a new collector introduced in Java 12. This new function was not announced in the official JEP, as it was a minor change request with the heading " Create Collector, which merges the results of two other collectors ". It is designed to combine results from two collectors.

All interesting - under the cut

If you still do not know what collectors are
Collectors are special classes used to convert a stream to another data structure. For example, in list:
list = Stream.of(1,2,3).collect(Collectors.toList());
//в листе теперь находятся элементы 1, 2 и 3

This is a useless example, because it creates a new stream and immediately transforms it. It is thought up to show use of collectors


Click here to view Collectors#teeing. According to official documentation:
“... returns a collector made up of two subordinate collectors. Each element transferred to the resulting collector is processed by both subordinate collectors, and then their results are combined using a special function that connects them to the final result. ”
"... returns a Collector that is a composite of two downstream collectors. 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."
Method Header:

static  Collector teeing(
Collector downstream1, Collector downstream2, BiFunction merger)

Interesting fact

This is a tee (teeing from English):

Teeingdescended from a tee. According to Wikipedia, “a tee is the most common fitting (connecting part of a pipeline, approx. Translator) used to combine [or split] a fluid stream (in this case, we mean streams, stream - stream / stream, comment translator).”
Other names were proposed: bisecting (dividing into 2_parts), duplexing, bifurcate (dividing), replicator, fanout (dividing), tapping, unzipping, collectionToBothAndThen, biCollecting, expanding (extension), forking, etc.
All alternatives rated by Core developers can be found here .

Examples of using

I have compiled three examples of using code with different levels of complexity.

Guest list

We extract two different types of information from the list of objects in the stream. Each guest must accept the invitation and can lead the family. We want to know who confirmed the reservation and the total number of participants (including guests and family members).

var result =
  // Guest(String name, boolean participating, Integer participantsNumber)
  new Guest("Marco", true, 3),
  new Guest("David", false, 2),
  new Guest("Roger",true, 6))
    // Первый коллектор, мы выбираем только тех, кто подтвердил участие
       // мы хотим взять только имя в списке
       Collectors.mapping(o ->, Collectors.toList())),
       // второй коллектор, мы хотим найти общее количество участников
       // мы объединяем коллекторы в новый объект,
       // значения передаются неявно
  // Результат
  // EventParticipation { guests = [Marco, Roger],
  // total number of participants = 11 }

class Guest {
  private String name;
  private boolean participating;
  private Integer participantsNumber;
  public Guest(String name, boolean participating,
   Integer participantsNumber) { = name;
    this.participating = participating;
    this.participantsNumber = participantsNumber;
  public boolean isParticipating() {
    return participating;
  public Integer getParticipantsNumber() {
    return participantsNumber;

class EventParticipation {
  private List guestNameList;
  private Integer totalNumberOfParticipants;
  public EventParticipation(List guestNameList,
   Integer totalNumberOfParticipants) {
    this.guestNameList = guestNameList;
    this.totalNumberOfParticipants = totalNumberOfParticipants;
public String toString() {
  return "EventParticipation { " +
    "guests = " + guestNameList +
    ", total number of participants = " + totalNumberOfParticipants +
    " }";

Filter names in two different lists

In this example, we split the stream of names into two lists according to the filter.

var result =
  Stream.of("Devoxx", "Voxxed Days", "Code One", "Basel One",
     "Angular Connect")
  // первый коллектор
  Collectors.filtering(n -> n.contains("xx"), Collectors.toList()),
  // второй коллектор
  Collectors.filtering(n -> n.endsWith("One"), Collectors.toList()),
  // слияние - автоматический вывод типа здесь не работает
  (List list1, List list2) -> List.of(list1, list2)
  System.out.println(result); // -> [[Devoxx, Voxxed Days], [Code One, Basel One]]

Count and add a stream of numbers

You may have seen a similar example appearing on blogs that combine sum and count to get the average. This example does not require Teeing, and you can just use a AverageIntsimple collector.

The following example uses functions from Teeingto return two values:

var result =
  Stream.of(5, 12, 19, 21)
      // первый коллектор
      // второй коллектор
      Collectors.summingInt(n -> Integer.valueOf(n.toString())),
      // объединение: (count, sum) -> new Result(count, sum);
  System.out.println(result); // -> {count=4, sum=57}

class Result {
  private Long count;
  private Integer sum;
  public Result(Long count, Integer sum) {
    this.count = count;
    this.sum = sum;
  public String toString() {
    return "{" +
      "count=" + count +
      ", sum=" + sum +

Possible trap


Many examples are used Map.Entryto store the result BiFunction. Please do not do this because you cannot store the last argument in Map. There is no standard object in Java Core for storing two values ​​- you will have to create it yourself.

All About New Java 12 Features

You can find out more information and interesting facts about Java 12 in this presentation .

Successful collectibles!

Also popular now: