New Stream Collectors in Java 9

1. Overview

Collectors were added in Java 8 which helped accumulate input elements into mutable containers such as MapList, and Set.

In this article, we’re going to explore two new collectors added in Java 9: Collectors.filtering and Collectors.flatMapping used in combination with Collectors.groupingBy providing intelligent collections of elements.

2. Filtering Collector

The Collectors.filtering is similar to the Stream filter(); it’s used for filtering input elements but used for different scenarios. The Stream’s filter is used in the stream chain whereas the filtering is a Collector which was designed to be used along with groupingBy.

With Stream’s filter, the values are filtered first and then it’s grouped. In this way, the values which are filtered out are gone and there is no trace of it. If we need a trace then we would need to group first and then apply filtering which actually the Collectors.filtering does.

The Collectors.filtering takes a function for filtering the input elements and a collector to collect the filtered elements:

@Test
public void givenList_whenSatifyPredicate_thenMapValueWithOccurences() {
    List<Integer> numbers = List.of(1, 2, 3, 5, 5);

    Map<Integer, Long> result = numbers.stream()
      .filter(val -> val > 3)
      .collect(Collectors.groupingBy(i -> i, Collectors.counting()));

    assertEquals(1, result.size());

    result = numbers.stream()
      .collect(Collectors.groupingBy(i -> i,
        Collectors.filtering(val -> val > 3, Collectors.counting())));

    assertEquals(4, result.size());
}

3. FlatMapping Collector

The Collectors.flatMapping is similar to Collectors.mapping but has a more fine-grained objective. Both the collectors takes a function and a collector where the elements are collected but flatMapping function accepts a Stream of elements which is then accumulated by the collector.

Let’s see the following model class:

class Blog {
    private String authorName;
    private List<String> comments;
      
    // constructor and getters
}

Collectors.flatMapping lets us skip intermediate collection and write directly to a single container which is mapped to that group defined by the Collectors.groupingBy:

@Test
public void givenListOfBlogs_whenAuthorName_thenMapAuthorWithComments() {
    Blog blog1 = new Blog("1", "Nice", "Very Nice");
    Blog blog2 = new Blog("2", "Disappointing", "Ok", "Could be better");
    List<Blog> blogs = List.of(blog1, blog2);
        
    Map<String,  List<List<String>>> authorComments1 = blogs.stream()
     .collect(Collectors.groupingBy(Blog::getAuthorName, 
       Collectors.mapping(Blog::getComments, Collectors.toList())));
       
    assertEquals(2, authorComments1.size());
    assertEquals(2, authorComments1.get("1").get(0).size());
    assertEquals(3, authorComments1.get("2").get(0).size());

    Map<String, List<String>> authorComments2 = blogs.stream()
      .collect(Collectors.groupingBy(Blog::getAuthorName, 
        Collectors.flatMapping(blog -> blog.getComments().stream(), 
        Collectors.toList())));

    assertEquals(2, authorComments2.size());
    assertEquals(2, authorComments2.get("1").size());
    assertEquals(3, authorComments2.get("2").size());
}

The Collectors.mapping maps all grouped author’s comments to the collector’s container i.e. List whereas this intermediate collection is removed with flatMapping as it gives a direct stream of the comment list to be mapped to the collector’s container.

4. Conclusion

This article illustrates the use of the new Collectors introduced in Java9 i.e. Collectors.filtering() and Collectors.flatMapping() used in combination with Collectors.groupingBy().

These Collectors can also be used along with Collectors.partitioningBy() but it only creates two partitions based on conditions and the real power of the collectors isn’t leveraged; hence left out of this tutorial.

The complete source code for the code snippets in this tutorial is available over on GitHub.

Related posts:

Spring Data Java 8 Support
Spring Boot - Interceptor
Java Program to Check Cycle in a Graph using Graph traversal
Làm thế nào tạo instance của một class mà không gọi từ khóa new?
Java Program to Implement Bloom Filter
Java Program to Implement LinkedTransferQueue API
Login For a Spring Web App – Error Handling and Localization
Java Program to Implement the Hungarian Algorithm for Bipartite Matching
A Custom Media Type for a Spring REST API
Injecting Prototype Beans into a Singleton Instance in Spring
Java Program to Generate Random Numbers Using Probability Distribution Function
Java Program to Implement Euler Circuit Problem
Java Program to Perform Preorder Non-Recursive Traversal of a Given Binary Tree
Java Program to Find Transpose of a Graph Matrix
Java Program to Implement Variable length array
Java Program to Implement Best-First Search
Java Program to add two large numbers using Linked List
Hướng dẫn Java Design Pattern – Interpreter
Notify User of Login From New Device or Location
Lập trình hướng đối tượng (OOPs) trong java
Using Spring ResponseEntity to Manipulate the HTTP Response
Hướng dẫn Java Design Pattern – Null Object
Java Collections Interview Questions
Convert Hex to ASCII in Java
Java Program to Generate All Possible Combinations Out of a, b, c, d, e
Spring Boot - Application Properties
Hashing a Password in Java
Java Program to Check Whether it is Weakly Connected or Strongly Connected for a Directed Graph
Java Program to Implement the Monoalphabetic Cypher
Remove All Occurrences of a Specific Value from a List
Java Program to Generate a Random UnDirected Graph for a Given Number of Edges
Working with Tree Model Nodes in Jackson