I have a bunch of predicates, and I want to chain them together with logical "and" so that the final result is true only if all of the individual predicates evaluate to true.
As I see it, there are two ways I can write this. I can chain them together like this:
Predicate composedPredicate =
predicate1
.and(predicate2)
.and(predicate3)
.and(predicate4)
Or I can use a more nested approach like this:
Predicate composedPredicate =
predicate1
.and(predicate2
.and(predicate3
.and(predicate4)))
Obviously, option 1 is more readable, but it seems like it might be slightly less efficient. I imagine option 1 is roughly equivalent to:
(((p1 && p2) && p3) && p4)
While option 2 would be:
(p1 && (p2 && (p3 && p4)))
In the second case, the first argument p1
would be evaluated and if it is false, the whole thing immediately short-circuits and you are done. In the first option, the first argument is actually the whole expression ((p1 && p2) && p3)
, which itself has the first argument of (p1 && p2)
, which in turn has p1
as its first argument. Basically, you're going 2 more "steps" up the stack before p1 actually can be evaluated. I don't actually know how Java implements the default predicate methods so correct me if I'm wrong here.
Is there any way to get the best of both worlds? If not, should I favor the more readable approach over what is probably a very marginal performance increase or vise versa?
Generally, favor readability any perceived marginal performance considerations. For most predicates, we would expect them to execute very quickly and it would be hard to notice such composed predicates that short circuit early and often versus those that short circuit late or not at all.
So start with your first option, the more readable one, that isn't nesting the predicates to mess with the order of operations.
If you do find that there is a performance concern, due to some kind of profiler stats or benchmark performance run, then you can reorder the operations for a performance benefit.
and
between them, these would be the ones that are more likely to return false
.But this also shouldn't affect readability. You shouldn't need to nest the predicates as you have done; at most you would need to reorder them, e.g.
Predicate composedPredicate =
predicate4
.and(predicate2)
.and(predicate1)
.and(predicate3)
if you notice that predicate4
rarely returns true
and predicate3
is computationally intensive.