如何将多个谓词应用于java.util.Stream's
filter()
方法?
这就是我现在所做的,但我并不喜欢它。我有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());
我知道如果我事先了解过滤器的数量,我可以这样做:
List<Thing> filtered = things.parallelStream().filter(filter1).or(filter2).or(filter3)).collect(Collectors.toList());
但是如何在不混合编程风格的情况下应用未知数量的谓词?知道它看起来有点难看......
答案 0 :(得分:47)
如果你有一个Collection<Predicate<T>> filters
,你总是可以使用名为 reduction 的过程从中创建一个谓词:
Predicate<T> pred=filters.stream().reduce(Predicate::and).orElse(x->true);
或
Predicate<T> pred=filters.stream().reduce(Predicate::or).orElse(x->false);
取决于您希望如何组合过滤器。
如果orElse
调用中指定的空谓词集合的回退符合标识角色(x->true
为and
谓词执行,而x->false
执行or
{1}} ing)您也可以使用reduce(x->true, Predicate::and)
或reduce(x->false, Predicate::or)
来获取过滤器,但对于非常小的集合来说效率稍低,因为它总是会将标识谓词与集合的谓词结合起来,即使它包含只有一个谓词。相反,如果集合的大小为reduce(accumulator).orElse(fallback)
,则上面显示的变体1
将返回单个谓词。
请注意此模式如何也适用于类似问题:拥有Collection<Consumer<T>>
您可以使用
Consumer<T>
Consumer<T> c=consumers.stream().reduce(Consumer::andThen).orElse(x->{});
等
答案 1 :(得分:38)
我假设您的Filter
是与java.util.function.Predicate
不同的类型,这意味着它需要适应它。一种可行的方法是这样的:
things.stream().filter(t -> filtersCollection.stream().anyMatch(f -> f.test(t)));
这会为每个谓词评估重新创建过滤器流带来轻微的性能损失。为了避免这种情况,你可以将每个过滤器包装成Predicate
并组成它们:
things.stream().filter(filtersCollection.stream().<Predicate>map(f -> f::test)
.reduce(Predicate::or).orElse(t->false));
但是,由于现在每个过滤器都在它自己的Predicate
后面,引入了一个更多级别的间接,所以不明确哪种方法会有更好的整体性能。
如果没有适应性问题(如果您的Filter
碰巧是Predicate
),问题陈述会变得更加简单,第二种方法显然会胜出:
things.stream().filter(
filtersCollection.stream().reduce(Predicate::or).orElse(t->true)
);
答案 2 :(得分:8)
我设法解决了这样的问题,如果用户想在一个过滤操作中应用一个谓词列表,一个可以是动态但没有给出的列表,应该减少为一个谓词 - 就像这样:
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;
}
}
请注意,这会将它们与&#34; AND&#34;逻辑运算符。 与&#34; OR&#34;结合使用减少线应该是:
Predicate<T> p = Stream.of(predicates).reduce(x -> false, Predicate::or);
答案 3 :(得分:6)
这是一种解决此问题的有趣方法,(直接从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());
编辑:这里是如何处理谓词ToIgnore是谓词列表的循环。我从中创建了一个谓词谓词ToIgnore。
Predicate<T> predicateToIgnore = null;
for (Predicate<T> predicate : predicatesToIgnore) {
predicateToIgnore = predicateToIgnore == null ? predicate : predicateToIgnore.or(predicate);
}
然后,使用此单个谓词进行过滤。这创建了一个更好的过滤器恕我直言