Java streams 37. Collect 13. Collectors.flatMapping() 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.



Creating Map object using Collectors.flatMapping() collector

The Collectors.flatMapping() collector (introduced with Java 9) transforms each stream element into a stream of values according to the specified function and then collects the emitted values using the specified collector:

Collector<T,?,R> mapping(Function<T, Stream<U>> mapper, Collector<U,A,R> downstream) – returns a Collector which generates a stream (of type U) from each element of type T and then accumulates the U values using the downstream reduction.

The downstream is a collector that collects U values into a structure of type R.

Before demonstrating the Collectors.flatMapping() collector usage, let us review how the flatMap() operation works. For example: 

   
  List<Character> list = Stream.of("cat", "fish", "cat", "dog")
                               .flatMap(s -> s.chars()
                               .mapToObj(c -> (char)c))
                               .collect(Collectors.toList());
  System.out.print(list);        
             //prints: [c, a, t, f, i, s, h, c, a, t, d, o, g]
    

As you can see, the flatMap() operation creates a stream (of characters, in this case) from each element and then accumulated the results (in a List, in this example).

The same result can be achieved using Collectors.flatMapping() collector: 

    
  List<Character> map2 = Stream.of("cat", "fish", "cat", "dog")
   .collect(Collectors
             .flatMapping(s -> s.chars()
                                .mapToObj(c -> (char)c), 
                                       Collectors.toList()));
  System.out.print(list); 
            //prints: [c, a, t, f, i, s, h, c, a, t, d, o, g]
     

If necessary, we can add to the stream (generated by flatMapping()) any operation we need to transform the elements before collecting them. For example, we can exclude character ‘a‘ from the stream: 

   
  List<Character> list = Stream.of("cat", "fish", "cat", "dog") 
   .collect(Collectors 
            .flatMapping(s -> s.chars()
                               .mapToObj(c -> (char)c)
                               .filter(c -> !c.equals('a')), 
                                       Collectors.toList())); 
  System.out.print(list); 
                  //prints: [c, t, f, i, s, h, c, t, d, o, g]
      

As you can see, the result does not contain the character ‘a‘ anymore.

These examples do not justify the Collectors.flatMapping() collector usage. We could easily get the same result using flatMap() operation.

But let us say we would like to reduce the stream to a Map that has element length as a key and the List of all the characters of the values of the same length as the Map value like this: Stream.of(“cat”, “fish”, “cat”, “dog”) => {3=[c, a, t, c, a, t, d, o, g], 4=[f, i, s, h]}

To do it using flatMap() or any of the operations we have discussed already is not possible, because we need to map each element to two values (length and the list of characters) at the same time. That is where the Collectors.flatMapping() collector comes handy:

     
  Map<Integer, List<Character>> map = 
                    Stream.of("cat", "fish", "cat", "dog")
    .collect(Collectors.groupingBy(String::length, 
             Collectors.flatMapping(s -> s.chars()
                                  .mapToObj(c -> (char)c), 
                                    Collectors.toList())));
  System.out.print(map);        
  //prints: {3=[c, a, t, c, a, t, d, o, g], 4=[f, i, s, h]}
     

That is what the Collectors.flatMapping() collector does: it applies to each stream element the specified function that generates a stream and accumulates the emitted elements.

Similarly, the Collectors.flatMapping() collector can be used with the Collectors.partitioningBy() collector:

  
  Map<Boolean, List<Character>> map = 
                    Stream.of("cat", "fish", "cat", "dog")
   .collect(Collectors.partitioningBy(s -> s.length() == 3, 
       Collectors.flatMapping(s -> s.chars()
                             .mapToObj(c -> (char)c)
                             .filter(c -> !c.equals('a')), 
                                     Collectors.toList())));
  System.out.print(map);        
  //prints: {false=[f, i, s, h], true=[c, t, c, t, d, o, g]}
    

In the next post, we will talk about creating one value from a stream using Collectors.reducing() collector (typically used as a parameter for Collectors.groupingBy() or Collectors.partitioningBy() 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