The Predicate or() method is used to combine a Predicate instance with another, to compose a third Predicate instance. The composed Predicate will return true if either of the Predicate instances it is composed from return true , when their test() methods are called with same input parameter as the composed Predicate .
More filters can be applied in a variety of methods, such using the filter() method twice or supplying another predicate to the Predicate.
The and() method returns a composed predicate that represents a short-circuiting logical AND of given predicate and another. When evaluating the composed predicate, if the first predicate is false , then the other predicate is not evaluated.
If you have a Collection<Predicate<T>> filters
you can always create a single predicate out of it using the process called 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
.
Note how this pattern applies to similar problems as well: Having a Collection<Consumer<T>>
you can create a single Consumer<T>
using
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. 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:
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.
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:
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. To combine with an "OR" the reduce line should be:
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/). 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. I create a single predicate predicateToIgnore from it.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With