Stream API
Introduced in Java 8, the Stream API provides a functional-style way to process sequences of elements — filtering, transforming, and combining data — without writing explicit loops and mutable accumulator variables. Streams pair naturally with the lambdas and method references covered on the previous pages.
A stream is not a data structure
Because a stream holds no data of its own, it cannot be indexed, cannot be iterated more than once, and does not support adding or removing elements the way a List or Set does. Its entire job is to describe a sequence of operations, which only run when you ask for a result.
The three-part pipeline
Every stream pipeline has the same three-part shape:
Source — where the elements come from, most often
.stream()on aCollection.Intermediate operations — steps like
.filter()or.map()that transform the stream; there can be zero, one, or many of these, chained together.Terminal operation — a single final step like
.collect()or.forEach()that actually produces a result and consumes the stream.
Creating a stream from a collection
List<String> names = List.of("Alice", "Bob", "Charlie", "Dave");
List<String> longNames = names.stream() // source
.filter(name -> name.length() > 3) // intermediate operation
.map(String::toUpperCase) // intermediate operation
.collect(Collectors.toList()); // terminal operation
System.out.println(longNames); // [ALICE, CHARLIE, DAVE]Streams are lazy
Nothing happens until a terminal operation runs
Stream<String> pipeline = names.stream()
.filter(name -> {
System.out.println("filtering " + name);
return name.length() > 3;
});
System.out.println("Pipeline built, nothing printed yet.");
long count = pipeline.count(); // NOW the filter actually runs, once per elementStreams are single-use
Reusing a consumed stream throws IllegalStateException
Stream<String> stream = names.stream(); stream.forEach(System.out::println); // consumes the stream stream.forEach(System.out::println); // throws IllegalStateException: stream has already been operated upon or closed
A
Streamis a pipeline of operations over a source, not a data structure — it stores nothing itself.Every pipeline follows source → intermediate operations → terminal operation.
Intermediate operations are lazy — they build the pipeline but do not execute until a terminal operation runs.
A stream can be consumed by exactly one terminal operation; reusing it after that throws
IllegalStateException.