ExampleDifficulty: Intermediate1 min

Streams Example: The Same Steps Over Lines Read From a File

The same filter, map and collect over a source that is not in memory and may be larger than memory.

A stream's source does not have to be a collection. Files.lines gives a stream of the lines of a file, read as the pipeline asks for them, and the same filter, map and toList work on it unchanged:

java
try (Stream<String> lines = Files.lines(path)) {
    List<String> refs = lines
            .filter(line -> !line.isBlank())
            .map(line -> line.split(",")[0])
            .toList();
}

There is no list here at all. Files.lines produces a stream over a file that is read as the pipeline pulls from it, which is why a file of ten million lines does not need ten million lines of memory.

Put this beside the same filter, map and toList over an in-memory list, and the shared fact comes out: neither source was read until the terminal operation, the final step that asks for a result, ran, and neither stream can be run a second time. A stream is a description of work over a source, and the source can be anything that can produce elements one at a time.