Java streams 41. Collect 17. Collectors.collectingAndThen() collector

Terminal operation either returns one value (of the same or another type than the input type) or does not return anything at all (produces only side effects). It does not allow another operation to be applied afterward and closes the stream.

In this post, we will continue covering the last of the terminal operations called collect():

R collect(Collector<T,A,R> collector)

It is a specialization of the reduce() operation. It allows implementing a vast variety of algorithms using the ready-to-use collectors from the java.util.stream.Collectors class. We discussed how to create a custom collector in Java streams 25. Collect 1. Custom collector. In this article, we will use only the collectors produced by the Collectors class.



Collectors.collectingAndThen()

The Collectors.collectingAndThen() collects stream elements and then applies the specified function to the result: 

Collector<T,A,RR> collectingAndThen​(Collector<T,A,R> downstream, Function<R,RR> finisher) – returns a Collector that accumulates the input elements using the downstream collector and then applies the finisher function to the result produced by the downstream collector.

The following code demonstrates the collector’s behavior: 

   
  List<String> synchronizedList = Stream.of("a", "b", "c")
    .collect(Collectors.
                 collectingAndThen(Collectors.toList(), 
                          Collections::synchronizedList));
  System.out.print(synchronizedList);   //prints: [a, b, c]
   

The above example is not very useful as the same result could be achieved without collectingAndThen(). For example: 

    
  List<String> list = Stream.of("a", "b", "c")
                        .collect(Collectors.toList());
  List<String> synchronizedList = 
                      Collections.synchronizedList(list);
  System.out.println(synchronizedList);  //prints: [a, b, c]
     

The collectingAndThen() collector is much more useful when used to create groupingBy() and partitioningBy() collectors. To demonstrate it, we are going to use Person class:

    
  class Person {
    private String name, city;
    private int age;

    public Person(String name, String city, int age) {
        this.name = name;
        this.city = city;
        this.age = age;
    }
    public String getCity() { return city; }
    public String getName() { return name; }
    public int getAge() { return age; }

    @Override
    public String toString() {
        return "Person{name='" + name + 
           "', city='" + city + "', age=" + age + '}';
    }
  }
    

And we are going to use a stream created of the following List<Person>

    
  List<Person> list = 
               List.of(new Person("John", "Denver", 25),
                       new Person("Jane", "Denver", 24),
                       new Person("Bob", "Denver", 23),
                       new Person("Bill", "Boston", 25),
                       new Person("Bob", "Chicago", 24),
                       new Person("Jill", "Boston", 23));
    

Consider example of Collectors.groupingBy() collector used to find the oldest person in each city:

    
  Map<String, Optional<Person>> map = list.stream()
     .collect(Collectors.groupingBy(Person::getCity, 
                          Collectors.maxBy(Comparator
                           .comparing(Person::getAge))));
  System.out.print(map);  
  //prints: {Chicago=Optional[Person{name='Bob', city='Chicago', age=24}],
  //         Denver=Optional[Person{name='John', city='Denver', age=25}],
  //         Boston=Optional[Person{name='Bill', city='Boston', age=25}]}

     

Using Collectors.collectingAndThen() collector, we can additional processing of the result and extract the name from the Optional<Person> object (result for each city):

     
  Map<String, String> map = list.stream()
    .collect(Collectors.groupingBy(Person::getCity,
            Collectors.collectingAndThen(Collectors.
           maxBy(Comparator.comparing(Person::getAge)),
       p -> p.orElse(new Person("x", "x", 0)).getName())));
  System.out.print(map);  
         //prints: {Chicago=Bob, Denver=John, Boston=Bill}
    

That is the primary purpose of Collectors.collectingAndThen() collector creation – to add another processing step (finisher) to the result produced by the Collectors.groupingBy() or Collectors.partitioningBy() collector.

In the next post, we will discuss the functionality and usage of the Collectors.teeing() collector.

See other posts on Java 8 streams and posts on other topics.
You can also use navigation pages for Java stream related blogs:
— Java 8 streams blog titles
— Create stream
— Stream operations
— Stream operation collect()
The source code of all the code examples is here in GitHub

, ,

Send your comments using the link Contact or in response to my newsletter.
If you do not receive the newsletter, subscribe via link Subscribe under Contact.

Powered by WordPress. Designed by Woo Themes