How to apply multiple Filters on Java Stream?

后端 未结 4 728
梦毁少年i
梦毁少年i 2021-01-11 09:27

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().filt

4条回答
  •  别那么骄傲
    2021-01-11 10:14

    A more general approach is to create a multi filter (Predicate) which is concatenated using Predicate.and(...) or Predicate.or(...). This is applicable to anything using Predicate - first of all Stream and Optional.
    Since the result is a Predicate itself one can continue with Predicate.and(...) or Predicate.or(...) or with building more complex predicates using MultiPredicate again.

    class MultiPredicate {
    
        public static  Predicate matchingAll(Collection> predicates) {
            Predicate multiPredicate = to -> true;
            for (Predicate predicate : predicates) {
                multiPredicate = multiPredicate.and(predicate);
            }
    
            return multiPredicate;    
        }
    
        @SafeVarargs
        public static  Predicate matchingAll(Predicate first, Predicate... other) {
            if (other == null || other.length == 0) {
                return first;
            }
    
            Predicate multiPredicate = first;
            for (Predicate predicate : other) {
                multiPredicate = multiPredicate.and(predicate);
            }
    
            return multiPredicate;
        }
    
        public static  Predicate matchingAny(Collection> predicates) {
            Predicate multiPredicate = to -> false;
            for (Predicate predicate : predicates) {
                multiPredicate = multiPredicate.or(predicate);
            }
    
            return multiPredicate;    
        }
    
        @SafeVarargs
        public static  Predicate matchingAny(Predicate first, Predicate... other) {
            if (other == null || other.length == 0) {
                return first;
            }
    
            Predicate multiPredicate = first;
            for (Predicate predicate : other) {
                multiPredicate = multiPredicate.or(predicate);
            }
    
            return multiPredicate;
        }
    
    }
    

    Applying to the question:

    public static void main(String... args) {
        List list = new ArrayList<>();
        Map 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
    
        List> filters = filterMap.entrySet().stream()
                .map(filterMapEntry -> mapToFilter(filterMapEntry))
                .collect(Collectors.toList());
    
        Predicate multiFilter = MultiPredicate.matchingAll(filters);
        List filtered = list.stream()
                .filter(multiFilter)
                .collect(Collectors.toList());
        for (TestObject to : filtered) {
            System.out.println("(" + to.getProperty(1) + "|" + to.getProperty(2) + "|" + to.getProperty(3) + ")");
        }
    
    }
    
    private static Predicate mapToFilter(Entry filterMapEntry) {
        return to -> to.getProperty(filterMapEntry.getKey()) ==  filterMapEntry.getValue();
    }
    

    In this case all filters have to match. The result is:

    (1|2|3)  
    

    If we use MultiPredicate.matchingAny(...) the result is:

    (1|2|3)  
    (1|2|4)  
    (1|4|3)  
    

提交回复
热议问题