Stream<T>represents a sequence of elements supporting functional-style operations.- It extends
BaseStream<T, Stream<T>>. - Streams enable declarative, lazy, and efficient processing of collections and other data sources.
- Streams do not store data; they process data from a source (Collection, Array, I/O, etc.).
| Category | Description |
|---|---|
| Stream Creation | Static or default methods to create streams |
| Intermediate Operations | Lazy operations that return a new Stream (support chaining) |
| Terminal Operations | Trigger stream execution and produce results or side-effects |
| Short-Circuiting | Operations that may stop processing early |
| Parallel / Sequential | Control execution mode |
| Close & Resource | Cleanup and resource handling |
| Method | Example | Description |
|---|---|---|
Stream.of(T...) |
Stream.of(1, 2, 3) |
Create stream from values |
Stream.empty() |
Stream.empty() |
Create an empty stream |
Stream.generate(Supplier) |
Stream.generate(() -> rand.nextInt(100)).limit(5) |
Infinite lazy stream |
Stream.iterate(seed, f) |
Stream.iterate(1, n -> n + 2).limit(5) |
Infinite iterative stream |
Stream.iterate(seed, predicate, f) (Java 9+) |
Stream.iterate(1, n -> n <= 10, n -> n + 1) |
Bounded iteration |
These operations return a new Stream and are executed only when a terminal operation is invoked.
filter(Predicate<T>)– Filter elementsmap(Function<T,R>)– Transform elementsflatMap(Function<T,Stream<R>>)– Flatten nested streamsdistinct()– Remove duplicatessorted()/sorted(Comparator)– Sort elementspeek(Consumer)– Debug elements without consuminglimit(long)– Keep first N elementsskip(long)– Skip first N elementstakeWhile(Predicate)(Java 9+) – Take elements while condition holdsdropWhile(Predicate)(Java 9+) – Drop elements while condition holdsunordered()– Remove ordering constraints
Terminal operations consume the stream and produce a result or side-effect.
forEach(Consumer)– Process each elementforEachOrdered(Consumer)– Ordered processing (parallel streams)toArray()/toArray(IntFunction)– Convert to arrayreduce()– Reduce elements to a single valuecollect(Collector)– Mutable reduction (e.g.,toList)min(Comparator)/max(Comparator)– Find min/maxcount()– Count elementsanyMatch(Predicate)– Match any elementallMatch(Predicate)– Match all elementsnoneMatch(Predicate)– Match nonefindFirst()– Find first elementfindAny()– Find any element (parallel-friendly)
These operations may terminate stream processing early.
| Operation | Type |
|---|---|
anyMatch |
Terminal |
allMatch |
Terminal |
noneMatch |
Terminal |
findFirst |
Terminal |
findAny |
Terminal |
limit |
Intermediate |
takeWhile |
Intermediate |
| Method | Description |
|---|---|
parallel() |
Enable parallel execution |
sequential() |
Switch back to sequential |
isParallel() |
Check execution mode |
| Method | Usage |
|---|---|
close() |
Close stream and release resources |
onClose(Runnable) |
Register cleanup handler |
iterator()spliterator()unordered()parallel()sequential()isParallel()onClose()close()
Java Streams provide a pipeline of operations classified into creation, intermediate (lazy), and terminal (eager). Stream processing occurs only after a terminal operation is invoked, enabling efficient, declarative, and parallel data processing with short-circuiting support.
- Streams are single-use
- Streams are lazy
- No modification of original data
- Prefer streams for readability and functional style
- Avoid heavy logic in
peek()
Java Stream operations are broadly classified into Intermediate and Terminal operations based on when they execute, what they return, and how they affect the stream lifecycle.
Understanding this difference is critical for:
- Correct Stream usage
- Writing efficient code
- Clearing Java interview questions confidently
Intermediate operations are operations that:
- Transform a stream into another stream
- Are lazy in nature
- Do not trigger execution on their own
They are mainly used to build the stream pipeline.
- ✅ Return a
Stream<T> - ✅ Can be chained
- ✅ Executed only when a terminal operation is called
- ✅ Do not consume the stream
- ✅ Support lazy evaluation
- ❌ Do not produce a final result
Intermediate operations are recorded, not executed immediately.
The JVM waits until a terminal operation is encountered and then processes elements only as required.
This improves:
- ⚡ Performance
- 💾 Memory efficiency
- ⏹ Short-circuiting behavior
Stream<Integer> stream =
numbers.stream()
.filter(n -> {
System.out.println("filter: " + n);
return n > 10;
})
.map(n -> {
System.out.println("map: " + n);
return n * 2;
});Nothing is printed because there is no terminal operation.
Intermediate operations are lazy, return a Stream, and are used to build the processing pipeline.
| Operation | Purpose |
|---|---|
filter() |
Select elements based on condition |
map() |
Transform elements |
flatMap() |
Flatten nested streams |
distinct() |
Remove duplicates |
sorted() |
Sort elements |
peek() |
Debug stream elements |
limit() |
Restrict number of elements |
skip() |
Skip elements |
takeWhile() (Java 9+) |
Take elements while condition holds |
dropWhile() (Java 9+) |
Drop elements while condition holds |
unordered() |
Remove encounter order |
Terminal operations are operations that:
- ✅ Trigger execution of the stream pipeline
- ✅ Produce a final result or side-effect
- ✅ Close the stream
- ❌ Do not return a Stream
- ✅ Trigger processing of all intermediate operations
- ✅ Consume the stream
- ❌ Cannot be chained further
- ❌ Stream becomes invalid after execution
List<Integer> result =
numbers.stream()
.filter(n -> {
System.out.println("filter: " + n);
return n > 10;
})
.map(n -> {
System.out.println("map: " + n);
return n * 2;
})
.collect(Collectors.toList());Execution now happens element by element.
Terminal operations trigger execution of the stream pipeline and consume the stream.
| Operation | Result Type |
|---|---|
| forEach() | void |
| forEachOrdered() | void |
| collect() | Collection / Map |
| reduce() | Single value |
| toArray() | Array |
| count() | long |
| min() / max() | Optional |
| anyMatch() | boolean |
| allMatch() | boolean |
| noneMatch() | boolean |
| findFirst() | Optional |
| findAny() | Optional |
- Not executed immediately
- Stored internally as pipeline steps
- Execution starts only after a terminal operation is called
- Executes immediately
- Pulls data from the source
- Applies intermediate operations per element
- Produces the final result
Streams do not process all elements at once.
Processing flow:
- One element is pulled from the source
- Passed through all intermediate operations
- Consumed by the terminal operation
- Repeats for the next element
This design enables short-circuiting.
numbers.stream()
.filter(n -> n > 10)
.findFirst();Stops processing as soon as the first matching element is found.
| Feature | Intermediate Operation | Terminal Operation |
|---|---|---|
| Return Type | Stream | Value / Collection / Optional / void |
| Execution | Lazy | Eager |
| Triggers Processing | No | Yes |
| Stream Reusability | Stream continues | Stream is closed |
| Chaining | Allowed | Not allowed |
| Position in Pipeline | Middle | End |
| Performance Impact | Optimized via laziness | Executes full or partial pipeline |
int sum =
numbers.stream() // Stream creation
.filter(n -> n > 10) // Intermediate
.map(n -> n * 2) // Intermediate
.limit(3) // Intermediate (short-circuit)
.reduce(0, Integer::sum); // TerminalWithout the terminal operation, nothing executes.
Can a stream have multiple terminal operations?
No. A stream can have only one terminal operation.
Can we reuse a stream after a terminal operation?
No. It throws IllegalStateException.
Why is peek() an intermediate operation?
Because it returns a Stream and does not consume it.
Why is laziness important in Streams?
It improves performance, enables short-circuiting, and reduces memory usage.
Intermediate operations build a lazy processing pipeline and return streams, whereas terminal operations trigger execution, consume the stream, and produce the final result.
- Stream vs Collection
map()vsflatMap()findFirst()vsfindAny()- Parallel stream pitfalls
- Short-circuiting behavior
Happy Coding 🚀