简体   繁体   English

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

[英]How to apply multiple predicates to a java.util.Stream?

How can I apply multiple predicates to a java.util.Stream's filter() method? 如何将多个谓词应用于java.util.Stream's filter()方法?

This is what I do now, but I don't really like it. 这就是我现在所做的,但我并不喜欢它。 I have a Collection of things and I need to reduce the number of things based on the Collection of filters (predicates): Collection了一些东西,我需要根据过滤器Collection (谓词)减少事物的数量:

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

I know that if I knew number of filters up-front, I could do something like this: 我知道如果我事先了解过滤器的数量,我可以这样做:

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

But how can I apply unknown number of predicates without mixing programming styles? 但是如何在不混合编程风格的情况下应用未知数量的谓词? For know it looks sort of ugly... 知道它看起来有点难看......

If you have a Collection<Predicate<T>> filters you can always create a single predicate out of it using the process called reduction : 如果你有一个Collection<Predicate<T>> filters你总是可以使用名为reduction的过程从中创建一个谓词:

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

or 要么

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

depending on how you want to combine the filters. 取决于您希望如何组合过滤器。

If the fallback for an empty predicate collection specified in the orElse call fulfills the identity role (which x->true does for and ing the predicates and x->false does for or ing) you could also use reduce(x->true, Predicate::and) or reduce(x->false, Predicate::or) to get the filter but that's slightly less efficient for very small collections as it would always combine the identity predicate with the collection's predicate even if it contains only one predicate. 如果指定一个空的谓词集合后备orElse呼叫满足身份角色( x->true做了and荷兰国际集团的谓词和x->false确实为or荷兰国际集团),你也可以使用reduce(x->true, Predicate::and)reduce(x->false, Predicate::or)来获取过滤器但是对于非常小的集合效率稍低,因为它总是将标识谓词与集合的谓词组合在一起,即使它只包含一个谓词。 In contrast, the variant reduce(accumulator).orElse(fallback) shown above will return the single predicate if the collection has size 1 . 相反,如果集合的大小为1则上面显示的变量reduce(accumulator).orElse(fallback)将返回单个谓词。


Note how this pattern applies to similar problems as well: Having a Collection<Consumer<T>> you can create a single Consumer<T> using 注意这种模式如何也适用于类似的问题:拥有Collection<Consumer<T>>你可以使用创建一个Consumer<T>

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

Etc. 等等。

I am assuming your Filter is a type distinct from java.util.function.Predicate , which means it needs to be adapted to it. 我假设你的Filter是一个与java.util.function.Predicate不同的类型,这意味着它需要适应它。 One approach which will work goes like this: 一种可行的方法是这样的:

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

This incurs a slight performance hit of recreating the filter stream for each predicate evaluation. 这会为每个谓词评估重新创建过滤器流带来轻微的性能损失。 To avoid that you could wrap each filter into a Predicate and compose them: 为了避免这种情况,你可以将每个过滤器包装成一个Predicate并组成它们:

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

However, since now each filter is behind its own Predicate , introducing one more level of indirection, it is not clear-cut which approach would have better overall performance. 但是,由于现在每个过滤器都落后于它自己的Predicate ,引入了一个更多的间接层,因此哪种方法具有更好的整体性能并不明确。

Without the adapting concern (if your Filter happens to be a Predicate ) the problem statement becomes much simpler and the second approach clearly wins out: 如果没有适应性问题(如果你的Filter碰巧是一个Predicate ),问题陈述会变得更加简单,第二种方法显然胜出:

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

I've managed to solve such a problem, if a user wants to apply a list of predicates in one filter operation, a list which can be dynamic and not given, that should be reduced into one predicate - like this: 我设法解决了这样一个问题,如果用户想要在一个过滤操作中应用一个谓词列表,一个可以是动态但没有给出的列表,应该减少为一个谓词 - 如下所示:

public class TestPredicates {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        System.out.println(numbers.stream()
                .filter(combineFilters(x -> x > 2, x -> x < 9, x -> x % 2 == 1))
                .collect(Collectors.toList()));
    }

    public static <T> Predicate<T> combineFilters(Predicate<T>... predicates) {

        Predicate<T> p = Stream.of(predicates).reduce(x -> true, Predicate::and);
        return p;

    }
}

Note that this will combine them with an "AND" logic operator. 请注意,这将它们与“AND”逻辑运算符组合在一起。 To combine with an "OR" the reduce line should be: 要与“OR”组合,reduce行应该是:

Predicate<T> p = Stream.of(predicates).reduce(x -> false, Predicate::or);

This is an interesting way of solving this problem, (directly pasting from http://www.leveluplunch.com/java/tutorials/006-how-to-filter-arraylist-stream-java8/ ). 这是一种解决这个问题的有趣方法,(直接粘贴于http://www.leveluplunch.com/java/tutorials/006-how-to-filter-arraylist-stream-java8/ )。 I think this is a more efficient way. 我认为这是一种更有效的方式。

Predicate<BBTeam> nonNullPredicate = Objects::nonNull;
Predicate<BBTeam> nameNotNull = p -> p.teamName != null;
Predicate<BBTeam> teamWIPredicate = p -> p.teamName.equals("Wisconsin");

Predicate<BBTeam> fullPredicate = nonNullPredicate.and(nameNotNull)
        .and(teamWIPredicate);

List<BBTeam> teams2 = teams.stream().filter(fullPredicate)
        .collect(Collectors.toList());

EDIT: Here's how to deal with loops where predicatesToIgnore is a List of predicates. 编辑:这是如何处理谓词ToIgnore是谓词列表的循环。 I create a single predicate predicateToIgnore from it. 我从中创建了一个谓词predicateToIgnore。

Predicate<T> predicateToIgnore = null;
for (Predicate<T> predicate : predicatesToIgnore) {
    predicateToIgnore = predicateToIgnore == null ? predicate : predicateToIgnore.or(predicate);
}

Then, do a filter with this single predicate. 然后,使用此单个谓词执行过滤。 This creates a better filter IMHO 这创建了更好的过滤器恕我直言

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM