如何将多个谓词应用于 java.util.Stream?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24553761/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to apply multiple predicates to a java.util.Stream?
提问by Pawe? Dyda
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...
但是如何在不混合编程风格的情况下应用未知数量的谓词?要知道它看起来有点丑......
采纳答案by Marko Topolnik
I am assuming your Filter
is a type distinct from java.util.function.Predicate
, which means it needs to be adapted to it. One approach which will work goes like this:
我假设您Filter
的类型不同于java.util.function.Predicate
,这意味着它需要适应它。一种可行的方法是这样的:
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
恰好是 a Predicate
),问题陈述会变得更加简单,第二种方法显然胜出:
things.stream().filter(
filtersCollection.stream().reduce(Predicate::or).orElse(t->true)
);
回答by Holger
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. In contrast, the variant reduce(accumulator).orElse(fallback)
shown above will return the single predicate if the collection has size 1
.
如果指定一个空的谓词集合回退orElse
呼叫满足身份角色(这x->true
确实为and
荷兰国际集团的谓词和x->false
确实为or
荷兰国际集团),你也可以使用reduce(x->true, Predicate::and)
或reduce(x->false, Predicate::or)
获得的过滤器,但是这是略少效率非常小的集合,因为它会一直将身份谓词与集合的谓词结合起来,即使它只包含一个谓词。相反,reduce(accumulator).orElse(fallback)
如果集合具有 size ,则上面显示的变体将返回单个谓词1
。
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.
等等。
回答by Gunith D
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/). I think this is a more efficient way.
这是解决这个问题的有趣方法,(直接从http://www.leveluplunch.com/java/tutorials/006-how-to-filter-arraylist-stream-java8/粘贴)。我认为这是一种更有效的方法。
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. I create a single predicate predicateToIgnore from it.
编辑:以下是如何处理 predicatesToIgnore 是谓词列表的循环。我从中创建了一个谓词 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
然后,使用这个单一谓词进行过滤。这创造了一个更好的过滤器恕我直言
回答by Uziel Sulkies
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. To combine with an "OR" the reduce line should be:
请注意,这会将它们与“AND”逻辑运算符组合在一起。要与“OR”结合使用,reduce 行应该是:
Predicate<T> p = Stream.of(predicates).reduce(x -> false, Predicate::or);