Functional Programming & Streams
The Stream Pipeline
A stream is a lazily-evaluated pipeline over data: source → intermediate operations → one terminal operation. Nothing computes until the terminal runs; streams describe what, loops describe how.
- Anatomy: source (
stream(),Stream.of,Files.lines…) → intermediate ops → terminal op - Intermediate ops are lazy; the terminal op triggers a single fused pass
- A stream never modifies its source; it is single-use — consume once
- Prefer streams where they clarify; loops where they don't (EJ 45)
- Streams of unbounded sources work because of laziness + short-circuit ops (
limit,findFirst)
// How many long words?
long count = words.stream()
.filter(w -> w.length() > 12)
.count();
// Same, imperative:
int c = 0;
for (String w : words)
if (w.length() > 12) c++;Laziness is the engine: filter and map record intentions; count() runs the fused pipeline in one pass over the data, applying all stages per element. That is why Stream.iterate(0, n -> n + 1).map(...).limit(10) terminates — only 10 elements are ever pulled.
Stream.of("a", "b", "c");
Arrays.stream(intArray);
list.stream();
Stream.iterate(1L, n -> n * 2).limit(64); // powers of two
Stream.generate(Math::random).limit(5);
try (Stream<String> lines = Files.lines(path)) { // I/O-backed: close it!
long n = lines.count();
}