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