I'm trying to understand how the non-terminal streams operations are invoked.
Stream.of("aaa", "bbb", "ccc")
.map(s -> {
System.out.println(s);
return s.toUpperCase();
});
//prints nothing
Stream.of("aaa", "bbb", "ccc")
.map(s -> {
System.out.println(s);
return s.toUpperCase();
})
.forEach(s -> {});
//prints "aaa" "bbb" "ccc"
This seems clear for me.
The first stream is not ended with terminal operation, so it doesn't invoke the non-terminal operation and nothing is printed. The second one has terminal operation, so all the elements are printed.
Stream.of("aaa", "bbb", "ccc")
.map(s -> {
System.out.println(s);
return s.toUpperCase();
})
.findFirst();
//prints "aaa"
Stream.of("aaa", "bbb", "ccc")
.map(s -> {
System.out.println(s);
return s.toUpperCase();
})
.filter(s -> s.startsWith("B"))
.findFirst();
//prints "aaa" "bbb"
This is where I'm getting confused, especially with the last one. Looks like the stream works "backwards" in a sense. First it checks what elements are returned by terminal operation and next it does intermediate operations only for these elements. But how to explain the last one? Looks like it did the mapping for all the elements up to the first one that matches the filter. And in the last example, if I replace findFirst() with forEach(), it prints all the elements, even if it has only one element in the end.
Seems a bit counter-intuitive to me. Can anyone give me the proper explanation how the stream recognizes for what elements it should perform intermediate operations?