4

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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
pvpkiran
  • 25,582
  • 8
  • 87
  • 134

3 Answers3

4

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().

Edit

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
}
holi-java
  • 29,655
  • 7
  • 72
  • 83
3

If you are on Java 9 or higher, you could chain the optionals, applying Optional.filter, Optional.or and Optional.ifPresent:

abc().filter(n -> n > 5)
    .or(() -> def().filter(n -> n > 5))
    .or(() -> ghi().filter(n -> n > 5))
    .ifPresent(n -> {
        // do domething
    });

Note that this solution is complete, i.e. you don't need to check if any value is present, as this is already done by Optional.filter, Optional.or and Optional.ifPresent.

fps
  • 33,623
  • 8
  • 55
  • 110
1

Stream#anyMatch may not evaluated for all specific elements in predicate. But, at the same of also expects argument(s), which means they will be evaluated first.

Returns whether any elements of this stream match the provided predicate. May not evaluate the predicate on all elements if not necessary for determining the result. If the stream is empty then false is returned and the predicate is not evaluated.

Ravi
  • 30,829
  • 42
  • 119
  • 173