How to apply multiple Filters on Java Stream?

  • A+
Category:Languages

I have to filter a Collection of Objects by a Map, which holds key value pairs of the Objects field names and field values. I am trying to apply all filters by stream().filter().

The Objects are actually JSON, therefore the Map holds the names of its variables as well as the value they have to contain in order to be accepted, but for simplicity reasons and because its not relevant to the question I wrote a simple Testclass for simulating the behaviour:

public class TestObject {    private int property1;   private int property2;   private int property3;    public TestObject(int property1, int property2, int property3) {       this.property1 = property1;       this.property2 = property2;       this.property3 = property3;   }    public int getProperty(int key) {       switch(key) {           case 1: return property1;           case 2: return property2;           default: return property3;       }   } } 

What I have tried so far:

public static void main(String[] args) {     List<TestObject> list = new ArrayList<>();     Map<Integer, Integer> filterMap = new HashMap<>();     list.add(new TestObject(1, 2, 3));     list.add(new TestObject(1, 2, 4));     list.add(new TestObject(1, 4, 3));     filterMap.put(3, 3); //Filter property3 == 3     filterMap.put(2, 2); //Filter property2 == 2      //Does not apply the result     filterMap.forEach((key, value) -> list.stream()             .filter(testObject -> testObject.getProperty(key) == value)             .collect(Collectors.toList())     );     /* Gives error: boolean can not be converted to void     list = list.stream()             .filter(testObject -> filterMap.forEach((key, value) -> testObject.getProperty(key) == value))             .collect(Collectors.toList()             );     */     //Printing result      list.forEach(obj -> System.out.println(obj.getProperty(1) + " " + obj.getProperty(2) + " " + obj.getProperty(3))); } 

I tried putting forEach of the Map first and the stream of the Collection first, but both solutions did not work as intended. The desired output of this example would be only to print the object with the values property1=1, property2=2 and property3=3.

How can I apply all filters correctly like when you would put them one after another in the code with a fixed amount of filters?

With a known amount of filters:

list.stream().filter(...).filter(...) 

Edit:

Sweeper summed my question up very well in his answer, so just for clarification (and probably future readers) here again: I want to keep all Objects that satisfy all filters.

 


I suppose you want to keep all the TestObjects that satisfy all the conditions specified by the map?

This will do the job:

List<TestObject> newList = list.stream()         .filter(x ->                 filterMap.entrySet().stream()                         .allMatch(y ->                                 x.getProperty(y.getKey()) == y.getValue()                         )         )         .collect(Collectors.toList()); 

Translated into "English",

filter the list list by keeping all the elements x that:

  • all of the key value pairs y of filterMap must satisfy:
    • x.getProperty(y.getKey()) == y.getValue()

(I don't think I did a good job at making this human readable...) If you want a more readable solution, I recommend Jeroen Steenbeeke's answer.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: