Is map applied on all the list before filter findAny?

Viewed 566

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.

1 Answers

Stream::findAny is a short-circuiting terminal operation, which in a nutshell means that if an input is 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 immediatelly without processing of 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
Related