Reputation: 1902
I have this code snippet in Java:
List<Integer> li= Arrays.asList(1, 2, 3, null, 4, 5, null, 6, 7, 8);
li.stream().filter(e-> e!=null)
.map(e-> {System.out.print(" m "+ e); return e * 2;})
.filter(e-> e % 3 == 0)
.forEach(e-> {System.out.println(" fe " + e);});
The output is
m 1 m 2 m 3 fe 6
m 4 m 5 m 6 fe 12
m 7 m 8
I know filter, map are intermediate methods, which means they only start to work, when a terminal method like forEach is called. But I can't just wrap my mind around the exact order in which they are called and start to work in this case. Can someone explain to me how this output comes to be?
Upvotes: 4
Views: 177
Reputation: 44918
Let the code itself tell its story.
Here is your code, with additional printouts:
import java.util.*;
import java.util.stream.*;
public class StreamEvaluationOrder {
private static void p(String s) {
System.out.println(s);
}
public static void main(String[] args) {
List<Integer> li= Arrays.asList(1, 2, 3, null, 4, 5, null, 6, 7, 8);
li.stream()
.map(x -> { p("The element " + x + " is pulled from the list"); return x; })
.filter(e-> {
boolean res = e != null;
if (res) p(e + " is not null");
else p(e + " is null, therefore filtered out.\n");
return res;
})
.map(e-> {
int res = e * 2;
p(e + " is mapped to " + res);
return res;
})
.filter(e-> {
boolean res = e % 3 == 0;
if (res) p(e + " is divisible by three");
else p(e + " is not divisible by three, therefore filtered out. \n");
return res;
})
.forEach(e-> {
p(e + " reaches the forEach.\n");
});
}
}
Here is the story that it tells you:
The element 1 is pulled from the list
1 is not null
1 is mapped to 2
2 is not divisible by three, therefore filtered out.
The element 2 is pulled from the list
2 is not null
2 is mapped to 4
4 is not divisible by three, therefore filtered out.
The element 3 is pulled from the list
3 is not null
3 is mapped to 6
6 is divisible by three
6 reaches the forEach.
The element null is pulled from the list
null is null, therefore filtered out.
The element 4 is pulled from the list
4 is not null
4 is mapped to 8
8 is not divisible by three, therefore filtered out.
The element 5 is pulled from the list
5 is not null
5 is mapped to 10
10 is not divisible by three, therefore filtered out.
The element null is pulled from the list
null is null, therefore filtered out.
The element 6 is pulled from the list
6 is not null
6 is mapped to 12
12 is divisible by three
12 reaches the forEach.
The element 7 is pulled from the list
7 is not null
7 is mapped to 14
14 is not divisible by three, therefore filtered out.
The element 8 is pulled from the list
8 is not null
8 is mapped to 16
16 is not divisible by three, therefore filtered out.
The principle is that it pulls the elements from the source stream one by one, pushes each element through the entire pipeline, and only then begins to process the next element. Is it clearer now?
Upvotes: 6
Reputation: 271250
Think of a stream as a pipeline.
As soon as you call forEach
, the numbers start going through the pipeline. First comes 1
, then after it has gone through the pipeline, 2
comes, and so on.
1
comes and encounters the first filter
. Is it not null? It is! Continue! Now 1
reaches the map
, it is printed and doubled and becomes 2
! Continue! Now 2
(originally 1
) reaches the second filter
! Unfortunately it is not divisible by 3, so its journey stops right there!
The second number in the pipeline, 2
, now comes through. Roughly the same thing happens. It gets transformed to 4
which is not divisible by 3
. End of journey.
The third number 3
comes through, passes the first filter
, gets printed, then gets transformed to 6
, which is divisible by 3! So it passes the second filter
and gets printed in the forEach
! Hurray!
That's why fe 6
is printed.
Upvotes: 1
Reputation: 8758
m 1
: 1
passes first filter, then is transformed to 2
after print and then doesn't pass second filter.m 2
: all the same.m3 fe 6
: 3
passes first filter, then is transformed to 6
, then passes second filter.m 4
, m 5
: same as for 1
.m 6 fe 12
: same as for 3
.m 7
, m 8
: same as for 1
.The thing is that each element in stream goes through whole pipeline (or at least until it is not filtered our) and only after that the next element is picked up.
Upvotes: 1