Reputation: 2132
I found a quiz about Java 8 Stream API of peek method as below
Arrays.asList("Fred", "Jim", "Sheila")
.stream()
.peek(System.out::println)
.allMatch(s -> s.startsWith("F"));
The output is
Fred
Jim
I am confused how this stream works? My expected result should be
Fred
Jim
Sheila
The peek() method is an intermediate operation and it processes each element in Stream. Can anyone explain me this.
Upvotes: 9
Views: 1728
Reputation: 2072
As per Java Doc Of allMatch():
Returns whether all elements of this stream match the provided predicate. May not evaluate the predicate on all elements if not necessary for determining the result. If the stream is empty then {@code true} is returned and the predicate is not evaluated.
@apiNote
This method evaluates the universal quantification of the predicate over the elements of the stream (for all x P(x)). If the stream is empty, the quantification is said to be vacuously satisfied and is always {@code true} (regardless of P(x)).
predicate to apply to elements of this stream @return {@code true} if either all elements of the stream match the provided predicate or the stream is empty, otherwise {@code false}
In your case:
1-
p(x) : s -> s.startsWith("F")
X : "Fred"
result : X P(X) = true
2-
p(x) : s -> s.startsWith("F")
X : "Jim"
result : X P(X) = false
No further evaluation will take place, because X P(X) = false
boolean result = Arrays.asList("Fred", "Finda", "Fish")
.stream()
.peek(System.out::println)
.allMatch(s -> s.startsWith("F"));
System.out.println("Result "+result);
Output is :
Fred
Finda
Fish
Result true
Here stream processed completely because xP(x) = true from each element
Upvotes: 1
Reputation: 45309
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.
Upvotes: 9
Reputation: 40034
Arrays.asList("Fred", "Jim", "Sheila")
.stream()
.peek(System.out::println)
.allMatch(s -> s.startsWith("F"));
Fred
is printed. It matches soJim
is printed. It doesn't match so allMatch
terminates because "All didn't match"Upvotes: 4
Reputation: 51034
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.
Upvotes: 2