📜 ⬆️ ⬇️

New in Java 12: The Teeing Collector

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


All interesting - under the cut


If you still do not know what collectors are
Collectors are special classes that are used to convert a stream to another data structure. For example, in the 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 converts it. He coined in order to show the use of collectors

Documentation


Click here to view Collectors#teeing . According to official documentation:
“... returns a collector made up of two downstream collectors. Each element transferred to the resultant collector is processed by both downstream collectors, and then their results are combined using a special function that connects them to the final result. "
Original
"It is a combination of two downstream collectors. It’s a process that has been used for the final result."
Method Header:
')
 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) 

Interesting fact


This is a tee (teeing from English):



Teeing derived from tee. According to Wikipedia, “tee is the most common fitting (connecting part of a pipeline, approx. Translator) used to unite [or separate] the flow of liquids (in this case they mean stream, stream - stream / stream, approx. Translator)”.
Other names have been suggested: bisecting, duplexing, bifurcate (split), replicator, fanout, tapping, unzipping, collectionToBothAndThen, biCollecting, expanding (expanding), forking, etc.
All alternatives priced by Core developers can be found here .

Examples of using


I collected three examples of using code with different levels of complexity.

Guest list


We retrieve two different types of information from the list of objects in the stream. Each guest must accept the invitation and may bring a family. We want to know who confirmed the booking and the total number of participants (including guests and family members).

 var result = Stream.of( // Guest(String name, boolean participating, Integer participantsNumber) new Guest("Marco", true, 3), new Guest("David", false, 2), new Guest("Roger",true, 6)) .collect(Collectors.teeing( //  ,    ,    Collectors.filtering(Guest::isParticipating, //        Collectors.mapping(o -> o.name, Collectors.toList())), //  ,       Collectors.summingInt(Guest::getParticipantsNumber), //      , //    EventParticipation::new )); System.out.println(result); //  // EventParticipation { guests = [Marco, Roger], // total number of participants = 11 } 


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


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


Filter names in two different lists


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

 var result = Stream.of("Devoxx", "Voxxed Days", "Code One", "Basel One", "Angular Connect") .collect(Collectors.teeing( //   Collectors.filtering(n -> n.contains("xx"), Collectors.toList()), //   Collectors.filtering(n -> n.endsWith("One"), Collectors.toList()), //  -       (List<String> list1, List<String> 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 mean. This example does not require Teeing , and you can simply use AverageInt and a simple collector.

The following example uses functions from Teeing to return two values:

 var result = Stream.of(5, 12, 19, 21) .collect(Collectors.teeing( //   Collectors.counting(), //   Collectors.summingInt(n -> Integer.valueOf(n.toString())), // : (count, sum) -> new Result(count, sum); Result::new )); System.out.println(result); // -> {count=4, sum=57} 

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


Possible trap


Map.Entry

Many examples use Map.Entry to store the result of BiFunction . Please do not do this, because you cannot store the last argument in the Map . There is no standard object for storing two values ​​in Java Core — you will need to create it yourself.

All about new Java 12 features


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

Successful collectibles!

Source: https://habr.com/ru/post/445622/


All Articles