如何将多个谓词应用于java.util.Stream?

2022-08-31 17:09:41

如何将多个谓词应用于一个方法?java.util.Stream'sfilter()

这就是我现在所做的,但我真的不喜欢它。我有一个东西,我需要根据过滤器(谓词)减少事情的数量:CollectionCollection

Collection<Thing> things = someGenerator.someMethod();
List<Thing> filtered = things.parallelStream().filter(p -> {
   for (Filter f : filtersCollection) {
      if (f.test(p))
        return true;
   }
   return false;
}).collect(Collectors.toList());

我知道,如果我事先知道过滤器的数量,我可以做这样的事情:

List<Thing> filtered = things.parallelStream().filter(filter1).or(filter2).or(filter3)).collect(Collectors.toList());

但是,如何在不混合编程风格的情况下应用未知数量的谓词呢?因为知道它看起来有点丑陋...


答案 1

如果你有,你总是可以使用称为约简的过程从中创建一个谓词:Collection<Predicate<T>> filters

Predicate<T> pred=filters.stream().reduce(Predicate::and).orElse(x->true);

Predicate<T> pred=filters.stream().reduce(Predicate::or).orElse(x->false);

取决于您希望如何组合过滤器。

如果在调用中指定的空谓词集合的回退满足标识角色(用于谓词和 ing),则还可以使用或获取筛选器,但对于非常小的集合,效率会稍低,因为它始终将标识谓词与集合的谓词组合在一起,即使它只包含一个谓词。相反,如果集合具有 大小,则上面显示的变体将返回单个谓词。orElsex->trueandx->falseorreduce(x->true, Predicate::and)reduce(x->false, Predicate::or)reduce(accumulator).orElse(fallback)1


请注意此模式如何也适用于类似的问题:拥有 a,您可以使用Collection<Consumer<T>>Consumer<T>

Consumer<T> c=consumers.stream().reduce(Consumer::andThen).orElse(x->{});

等。


答案 2

我假设您的类型与 不同,这意味着它需要适应它。一种可行的方法是这样的:Filterjava.util.function.Predicate

things.stream().filter(t -> filtersCollection.stream().anyMatch(f -> f.test(t)));

这会导致为每个谓词评估重新创建筛选器流时性能略有影响。为了避免这种情况,您可以将每个过滤器包装成 a 并组合它们:Predicate

things.stream().filter(filtersCollection.stream().<Predicate>map(f -> f::test)
                       .reduce(Predicate::or).orElse(t->false));

但是,由于现在每个滤波器都落后于自己的 ,引入了另一个间接层,因此不清楚哪种方法具有更好的整体性能。Predicate

没有适应的顾虑(如果你碰巧是一个),问题陈述变得简单得多,第二种方法显然胜出:FilterPredicate

things.stream().filter(
   filtersCollection.stream().reduce(Predicate::or).orElse(t->true)
);

推荐