Given three functions like this:
private Optional<Integer> abc() {
return Optional.of(6);
}
private Optional<Integer> def() {
return Optional.of(3);
}
private Optional<Integer> ghi() {
return Optional.of(9);
}
If I want to check if one of the three functions return something which is greater than 5 (of course wrapped in Optional), in a traditional imperative style I would do it like this:
if( abc().get() > 5 || def().get() > 5 || ghi().get() > 5) {
......// Do something
} // I am not doing get() without checking ifPresent() just for simplicity sake
This would only go into function abc()
and skip def()
and ghi()
, because the first expression returns true. Which is a good optimization.
Now if I write the same in a functional style using Streams,
if( Stream.of(abc(), def(), ghi()).anyMatch(integer -> integer.get() > 5)) {
.........
}
I thought the same would happen, i.e. only abc()
will be called. But it calls all three functions. Isn't it redundant to check other two functions when there is anyMatch()
?
It is same in the case of noneMatch()
; the flow goes through whole Stream. I am just wondering: Isn't it really a bad thing to traverse the whole stream (especially if the stream has many values), even if the condition is met at the first element?
This is because the Stream#of
happens before Stream#anyMatch
, so all of the methods are called since they happens before Stream#of
.
You can make Stream#anyMatch
happens before actual method invocation by using Supplier<Optional<Integer>>
, for example:
// Note: it just create Suppliers and actual method is called on demand
Stream<Supplier<Optional<Integer>>> values=Stream.of(this::abc,this::def,this::ghi);
if(values.anyMatch(integer -> integer.get().get() > 5)) {
.........
}
As @FedericoPeraltaSchaffner already mentioned, the Optional
maybe empty you can just use Optional#orElse(0)
instead of Optional#get
, or use Opitional#filter(it -> it > 5).isPresent()
.
To illustrate the short-circuiting terminal operations of Stream
, you should use lambdas/method reference expressions since method call happens before Stream#of
, for example:
Supplier<Optional<Integer>> failsOnMismatched = () -> {
throw new IllegalStateException();
};
// the instantiation of method reference happen before `Stream#of`,
// but the linked method is called on demand.
// v
if(Stream.of(this::abc, failsOnMismatched).anyMatch(it -> it.get().orElse(0) > 5)){
//reached
}
//it is failed since the value of def() <= 5 ---v
if(Stream.of(this::def, failsOnMismatched).anyMatch(it -> it.get().orElse(0) > 5)){
//unreachable
}
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