The 30 second answer
Intermediate operations like filter and map do nothing on their own. Work only starts when a terminal operation runs, and then the stream does only as much as it needs. That is why short-circuit operations like findFirst, anyMatch or limit can stop early, even on an infinite stream. It also means a pipeline with no terminal operation never executes, so side effects inside it never happen.
Because intermediate operations are lazy, the stream can stop as soon as the answer is known:
Stream.iterate(1, i -> i + 1)
.filter(i -> i % 7 == 0)
.findFirst(); // Optional[7]; only 7 elements are examined
Stream.of("a", "b").peek(System.out::println); // prints nothing: no terminal opShort-circuiting operations are findFirst, findAny, anyMatch, allMatch, noneMatch and limit (plus takeWhile since Java 9). Laziness also lets the library fuse operations into a single pass instead of building intermediate collections.
A common trap: putting logic with side effects in peek or map and assuming it runs. Since Java 9, count() may skip executing the pipeline when it can compute the count directly from the source, and the Javadoc warns that side effects may then not run.
Sources: java.util.stream package summary: laziness and short-circuiting, Stream.count Javadoc)