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.