Reputation: 13845
I am executing the following program:
Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> {
System.out.println("filter: " + s);
return true;
})
.forEach(s -> System.out.println("forEach: " + s));
And the output I got is:
filter: d2
forEach: d2
filter: a2
forEach: a2
filter: b1
forEach: b1
filter: b3
forEach: b3
filter: c
forEach: c
However, I was expecting the following output:
filter: d2
filter: a2
filter: b1
filter: b3
filter: c
forEach: d2
forEach: a2
forEach: b1
forEach: b3
forEach: c
Meaning, first the filter
method loop should have executed completely and then the forEach
method loop should have started.
Is there anything I am doing wrong?
Upvotes: 7
Views: 10156
Reputation: 21586
The processing of a Stream will start with a terminal operation and consume one item after another. The advantages are:
The program has to calculate only relevant items - if for example only one item is needed, the filter
is only called once:
Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> {
System.out.println("filter: " + s);
return true;
})
.findAny()
.ifPresent(s -> System.out.println("forEach: " + s));
Output:
filter: d2
forEach: d2
You can even create infinte Streams (which would not be possible otherwise):
IntStream.iterate(0, i -> i+1)
.filter(s -> {
System.out.println("filter: " + s);
return true;
})
.peek(i -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
})
.forEach(s -> System.out.println("forEach: " + s));
Output:
filter: 0
forEach: 0
filter: 1
forEach: 1
... (up to infinity)
Upvotes: 1
Reputation: 393846
Your expectation is wrong.
When the terminal operation forEach
is executed, it consumes one element of the Stream
at a time. Each element it consumes has to pass all the intermediate operations of the Stream
, which causes filter
to be executed on the element just prior to forEach
being executed on same element (assuming the element passes the filter
).
In other words, filter
is lazily applied to each element just when the next operation in the Stream
pipeline requires its next element (in your case the next operation is forEach
).
This means that if your terminal operation would only require some of the Stream elements to be processed (for example, if you replaced forEach()
with findFirst()
), the filter()
operation would only be executed until the first element passes it (which in your example means the filter will be executed only for the first element).
Upvotes: 12