Search code examples
javajava-8java-streamshort-circuiting

Is map applied on all the list before filter findAny?


I want to filter a list for elements having a non null property and returning that property:

list.stream.map(a -> StringUtils.trimToEmpty(a.getProp())).filter( p -> StringUtils.isNotEmpty(p)).findAny().orElse("");

Do the code above first map all the elements? For efficiency reasons I'd like to process element by element.


Solution

  • Stream::findAny is a short-circuiting terminal operation, which in a nutshell means that if an item is present, it terminates the Stream.

    Ie. if the first element is qualified for the Stream::filter pipe and passes it through, the Stream::findAny returns it immediately without processing further elements in the original collection.

    There is a nice proof using Stream::peek to understand how Stream API works (let's say a class Foo has only one final String parameter prop):

    List<Foo> list = List.of(
       new Foo(""),            // the first is empty and doesn't pass through Stream::filter
       new Foo("one"),         // this is qualified
       new Foo("two"));        // this is qualified
    
    list.stream()
        .peek(item -> System.out.println("# Streamed    " + item.getProp())) // streamed
        .map(a -> StringUtils.trimToEmpty(a.getProp()))
        .peek(item -> System.out.println("#  Mapped     " + item))           // mapped
        .filter(StringUtils::isNotEmpty)
        .peek(item -> System.out.println("#   Filtered  " + item))           // filtered
        .findAny()
        .orElse("");
    

    The output shows the first element doesn't pass through Stream::filter (is empty) and ended before Stream::map. The second element passed through Stream::filter and the subsequent Stream::map and reached finally Stream::findAny. As long as Stream::findAny is a short-circuiting and terminal operation with a present result, it terminates the Stream.

    # Streamed    
    #  Mapped     
    # Streamed    one
    #  Mapped     one
    #   Filtered  one