Is Stream.findAny short circuit operation?

Consider this code

Object found = collection.stream()
    .filter( s -> myPredicate1(s))
    .filter( s -> myPredicate2(s))
    .findAny()

Will it process the entire stream and call both myPredicate1, and myPredicate2for all elements of the collection? Or will as many predicates be called up as needed to actually find the value?

+3
source share
5 answers

Yes, this is as indicated in Stream.findAny():

This is a short circuit operation.

It is a common misconception that objects in a stream are “pushing” toward consumption. Actually the opposite: the consumption operation pulls every element.

, . , , .

public class StreamFilterLazyTest {

  static int stI = 0;

  static class T { 

    public T() {
      super();
      this.i = ++stI;
    }

    int i;

    int getI() {
      System.err.println("getI: "+i);
      return i;
    }
  }

  public static void main(String[] args) {
    T[] arr = {new T(), new T(), new T(), new T(), new T(), new T(), new T(), new T(), new T(), new T()};
    Optional<T> found = Arrays.stream(arr).filter(t -> t.getI() == 3).findAny();
    System.out.println("Found: "+found.get().getI());
  }
}

:

getI: 1
getI: 2
getI: 3
Found: 3
+5

javadoc findAny() :

" .

" , , ..."

, findAny() "" , . , , .

javadoc :

" : , , filter(), , , . , .

, filter() , findAny() .

:

Q: + findAny ?

A: .

+4

, , , , , , . , findFirst, Stream .

findFirst .

, - parallelism, , , .

+2

Stream # findAny - . Predicate , # .

. ; , filter(), , , , . , .

@Holger , , :

if(predicate1.test(value) && predicate2.test(value)){
 ....
}

Test

Iterator<Predicate<Integer>> predicates = Stream.<Predicate<Integer>>of(
        it -> false,
        it -> {
            throw new AssertionError("Can't be short-circuited!");
        }
).iterator();

Predicate<Integer> expectsToBeShortCircuited = it -> predicates.next().test(it);

Stream.of(1).filter(expectsToBeShortCircuited).filter(expectsToBeShortCircuited)
      //                                       |
      //                                       |
      //                      here is short-circuited since the stream is empty now
      .findAny();
0

peek

== Sequential ==

Alpha1 Alpha2 Beta1 Beta2 Gamma1 Gamma2 Dolphin1 Fargo1 Fargo2 : Fargo: 9

== Parallel ==
Arnold1 Jim1 Loke1 Alpha1 Mustard1 Lenny1 Mustard2 Mark1 Alpha2 Mark2 Beta1 Beta2 Gamma1 Fargo1 Gamma2 Dolphin1 Fargo2 Found: Fargo Applications: 17

YMMV depending on the number of cores, etc.

Produced below

package test.test;

import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;

public class Snippet {

    static AtomicInteger predicateApplications;

    public static void main(String arr[]) {
        System.out.println("== Sequential == \n");
        sequential();
        System.out.println(" == Parallel == \n");
        parallel();
    }

    private static void sequential() {
        Stream<String> stream = Stream.of("Alpha", "Beta", "Gamma", "Dolphin", "Fargo", "Mustard", "Lenny", "Mark",
                "Jim", "Arnold", "Loke");
        execute(stream);
    }

    private static void parallel() {
        Stream<String> parallelStream = Stream
                .of("Alpha", "Beta", "Gamma", "Dolphin", "Fargo", "Mustard", "Lenny", "Mark", "Jim", "Arnold", "Loke")
                .parallel();
        execute(parallelStream);
    }

    private static void execute(Stream<String> stream) {
        predicateApplications = new AtomicInteger(0);

        Optional<String> findAny = stream.peek(s -> print(s + "1")).filter(s -> s.contains("a"))
                .peek(s -> print(s + "2")).filter(s -> s.startsWith("F")).findAny();

        String found = findAny.orElse("NONE");
        System.out.println("\nFound: " + found);
        System.out.println("Applications: " + predicateApplications.get());
    }

    private static void print(String s) {
        System.out.print(s + " ");
        predicateApplications.incrementAndGet();
    }

}
0
source

Source: https://habr.com/ru/post/1677909/


All Articles