Streams are lazy: nothing runs until a terminal operation
Intermediate operations (filter, map, peek, ...) just build up a pipeline description; none of them
execute a single element until a terminal operation (findFirst, collect, forEach, ...) pulls
values through. Terminal operations can also short-circuit, so a lazily-built pipeline may process far fewer
elements than the source size. Watch the interleaving of print statements below — it proves the laziness,
it doesn't just assert it.
import java.util.*;
import java.util.stream.*;
List<Integer> src = List.of(1, 2, 3, 4, 5);
var stream = src.stream()
.peek(x -> System.out.println(" peek saw " + x))
.filter(x -> x > 2);
System.out.println("stream pipeline built -- nothing printed above, because nothing has RUN yet");
Optional<Integer> first = stream.findFirst(); // terminal op, short-circuits after the first match
System.out.println("first element > 2: " + first + " <- only elements 1..3 were ever peeked at, not all 5");