So Java8
tries to speed up execution by doing all of the higher-order functions as soon as possible for any given input. That is, for example, if you call map
twice in a row on a list then it will access each element of the list only one time. This speeds it up because it goes from 2 passes through the list to one. To illustrate, take this simple example:
Stream.of(1, 2, 3)
.map(s -> {
System.out.println("map: " + s.toString());
return s;
})
.map(s -> {
System.out.println("map: " + s.toString());
return s;
})
This will print:
1
1
2
2
3
3
Because it is faster to 'touch' each element in the list one time than it is to iterate through the list fully for both map
s!
In terms of your example, let's take it piece by piece:
sort: a2; d2
sort: b1; a2
sort: b1; d2
sort: b1; a2
sort: b3; b1
sort: b3; d2
sort: c; b3
sort: c; d2
All of the sorting needs to happen at the same time, and it all needs to happen first. This is because the computer cannot know which element will be in which spot until the sorting is done (i.e. it can't do map on the same list location twice, because sort may change that)
Next, you essentially have this:
Stream.of("a2", "b1", "b3", "c", "d2")
.filter(s -> {
System.out.println("filter: " + s);
return s.startsWith("a");
})
.map(s -> {
System.out.println("map: " + s);
return s.toUpperCase();
})
.forEach(s -> System.out.println("forEach: " + s));
Now, to minimize passes throught he list, Java will go through each element in the list and execute the filter
and then the map
and then the forEach
. This is because none of these depend on the positions of the elements. In other words, Java sees that it can do all of these actions on each element, rather than by iterating through the whole list three time, for each function!
Now:
filter: a2
map: a2
forEach: A2
We filter
the first element, then we map
over it, then we do the final forEach
print.
filter: b1
filter: b3
filter: c
filter: d2
These all get filtered out, so the rest of the functions don't get called!