How peek() and allMatch() works together in Java 8 Stream API
The docs for the peek
method say (emphasis mine):
Returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream.
So in this case, peek
doesn't see "Sheila"
because that value is not consumed from the stream. As soon as "Jim"
was consumed, the result of .allMatch(s -> s.startsWith("F"))
is already known to be false
, so there is no need to consume any more elements from the stream.
Arrays.asList("Fred", "Jim", "Sheila")
.stream()
.peek(System.out::println)
.allMatch(s -> s.startsWith("F"));
- First time thru,
Fred
is printed. It matches so - Second time thru,
Jim
is printed. It doesn't match so allMatch terminates because "All didn't match" - So the last item was not consumed from the stream.
It's a stream optimization known as short-circuiting. Essentially, what happens is that allMatch
prevents the execution of unnecessary intermediate operations on the stream, because there is no point in performing them when the final result is known.
It's as though this happened:
take"Fred"
peek("Fred")
evaluate("Fred".startsWith("F"))
decide whether the result of allMatch() is known for sure: Not yet
take"Jim"
peek("Jim")
evaluate("Jim".startsWith("F"))
decide whether the result of allMatch() is known for sure: Yes
When "Jim".startsWith("F")
is evaluated, the result of allMatch(s -> s.startsWith("F"))
is known for certain. It doesn't matter what values come in the pipeline after "Jim"
, we know that all values start with "F" is false
This is not specific to the peek
/allMatch
combination, there are multiple intermediate and terminal short-circuiting operations. java.util.stream
package's docs state:
Further, some operations are deemed short-circuiting operations. An intermediate operation is short-circuiting if, when presented with infinite input, it may produce a finite stream as a result. A terminal operation is short-circuiting if, when presented with infinite input, it may terminate in finite time. Having a short-circuiting operation in the pipeline is a necessary, but not sufficient, condition for the processing of an infinite stream to terminate normally in finite time.
Extend this to finite streams, and short-circuiting operations obviate the execution of unnecessary pipeline steps, as in the case of your example.