Java streams 39. Collect 15. Collectors.filtering() 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.filering() collector

The Collectors.filtering() collector was introduced in Java 9. It filters the stream values and collects (reduces) the selected values into one structure:

Collector<T,?,R> filtering​(Predicate<T> predicate, Collector<T,A,R> downstream) – selects (filters) stream values using the Predicate<T> and collects the selected values into the final result (structure) R using the downstream Collector<T,A,R>.

The Collectors.filtering() collector can be used by passing into the collect() but is more useful as a parameter for the Collectors.groupingBy() and Collectors.partitioningBy().

In our code examples, we will be using the following Person class as a type of stream elements: 

    
  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 int getAge() { return age; }

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

And here is the list of Person objects we are going to stream in each of our examples: 

     
  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));
     

The following is the code example of how the Collectors.filtering() can be used to collect in List only persons of 25 years of age:

      
  List<Person> list1 = list.stream()
   .collect(Collectors.filtering(p -> p.getAge() == 25, 
                                   Collectors.toList()));
  System.out.print(list);  
    //prints: [Person{name='John', city='Denver', age=25}, 
    //         Person{name='Bill', city='Boston', age=25}]
    

But the same result can be achieved much simpler without Collector.filtering():

      
  List<Person> list = list.stream()
            .filter(p -> p.getAge() == 25)
            .collect(Collectors.toList());
  System.out.println(list);  
    //prints: [Person{name='John', city='Denver', age=25}, 
    //         Person{name='Bill', city='Boston', age=25}]
     

As you can see, there is no advantage of using the Collectors.filtering() collector for such a case.

That is because the Collectors.filtering() was created to be used with groupingBy() and partitioningBy() for multi-level grouping.

Before demonstrating how to do it, let us first refresh how groupingBy() and partitioningBy() work without the Collectors.filtering() collector:

     
  Map<String, List<Person>> map = list.stream()
        .collect(Collectors.groupingBy(Person::getCity));
  System.out.print(map);  
  //Prints:
  //  {Chicago=[Person{name='Bob', city='Chicago', age=24}],
  //   Denver=[Person{name='John', city='Denver', age=25},
  //           Person{name='Jane', city='Denver', age=24},
  //           Person{name='Bob', city='Denver', age=23}],
  //   Boston=[Person{name='Bill', city='Boston', age=25},
  //           Person{name='Jill', city='Boston', age=23}]}

  Map<Boolean, List<Person>> map = list.stream()
   .collect(Collectors
      .partitioningBy(p -> "Denver".equals(p.getCity())));
  System.out.println(map);  
  //prints: 
  //    {false=[Person{name='Bill', city='Boston', age=25}, 
  //            Person{name='Bob', city='Chicago', age=24}, 
  //            Person{name='Jill', city='Boston', age=23}], 
  //      true=[Person{name='John', city='Denver', age=25}, 
  //            Person{name='Jane', city='Denver', age=24}, 
  //            Person{name='Bob', city='Denver', age=23}]}
          

As you can see, the groupingBy() groups objects by the value specified as the parameter (Person.getCity(), in our example). The partitioningBy() groups objects in two categories: those that match the specified predicate (Person.getCity() has to return “Denver”, in our example) and those that do not match the predicate. 

Now let us add the Collectors.filtering() collector – the one we have demonstrated already – which collects in a List only persons of 25 years of age:

          
  Map<String, List<Person>> map = list.stream()
   .collect(Collectors.groupingBy(Person::getCity, 
             Collectors.filtering(p -> p.getAge() == 25, 
                                  Collectors.toList())));
  System.out.print(map);  
   //prints: {Chicago=[],
   //          Denver=[Person{name='John', city='Denver', age=25}],
   //          Boston=[Person{name='Bill', city='Boston', age=25}]}
         

As you can see, the provided Collectors.groupingBy() collects persons in the groups by city, then Collector.filtering() selects from each group the 25-years old only and collects them in a List. Since there is no such a person in Chicago, the list with “Chicago” key is empty.

It works similarly with Collectors.partitioningBy():

     
  Map<Boolean, List<Person>> map= list.stream()
   .collect(Collectors
      .partitioningBy(p -> "Denver".equals(p.getCity()), 
              Collectors.filtering(p -> p.getAge() == 25, 
                                    Collectors.toList())));
  System.out.println(map);  
  //prints: {false=[Person{name='Bill', city='Boston', age=25}],
  //          true=[Person{name='John', city='Denver', age=25}]}
       

In the next post, we will talk about creating unmodifiable structures from a stream using Collectors.toUnmodifiableList(), Collectors.toUnmodifiableSet(), and Collectors.toUnmodifiableMap() collectors.

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