BackendBench
Coffee

Unravel โ€บ Java concepts โ€บ Collections and streams

Java language

Streams

A stream is a pipeline over a sequence of values: start from a source, chain steps such as filter and map, and finish with a terminal step such as collect.

Learn these first

At a glance

Mental model

An assembly line.

What happens, in order
  1. 1Source: list.stream(), Arrays.stream(a), IntStream.range(0, n)Intermediate steps (filter, map, sorted, distinct, limit, flatMap) each return a new stream and do no work yet.
  2. 2A terminal step (collect, toList, forEach, count, reduce, anyMatch, findFirst) starts the pipeline and produces the result
  3. 3Elements flow through all steps one at a time (not step by step over the whole list), which lets findFirst or limit stop early
  4. 4A stream can be used onceA second terminal operation throws IllegalStateException.
Remember
Watch out
Expecting the stream to run without a terminal operationReusing a streamSide effects and shared mutable state in lambdasUsing parallelStream() by defaulttoMap with duplicate keys
Example (highlighted lines explain it)
List<String> shouted = names.stream()    .filter(n -> !n.isBlank())    .map(String::toUpperCase)    .toList();
  1. line 1 uses stream, the core of Streams
Read the full explanation (about 2 min)

A stream is a pipeline over a sequence of values: start from a source, chain steps such as filter and map, and finish with a terminal step such as collect. Nothing runs until the terminal step.

A mental model

An assembly line. Items pass through stations (filter out the bad ones, repaint, pack) one after another, and the line only starts moving when someone orders the finished boxes.

How it works

Source: list.stream(), Arrays.stream(a), IntStream.range(0, n). Intermediate steps (filter, map, sorted, distinct, limit, flatMap) each return a new stream and do no work yet.

A terminal step (collect, toList, forEach, count, reduce, anyMatch, findFirst) starts the pipeline and produces the result.

Elements flow through all steps one at a time (not step by step over the whole list), which lets findFirst or limit stop early.

A stream can be used once. A second terminal operation throws IllegalStateException.

Going deeper

Streams do not change the source collection; they build a new result. Keep lambdas free of side effects, especially with parallelStream(), where the order and threading are not yours to control.

A parallel stream uses the shared common fork-join pool. A blocking call inside it can slow unrelated parts of the application. It helps only for CPU-heavy work on big data; for small lists it is slower.

Collectors turn the result into the shape you want: toList, toSet, groupingBy (a Map of lists), counting, joining. Collectors.toMap throws on duplicate keys unless you give a merge function.

Readability beats cleverness: if a pipeline needs five nested lambdas, a simple loop is easier to debug.

Under the hood

A pipeline is a linked chain of stages. When the terminal step runs, a Spliterator produces elements and each stage wraps a Sink that passes the element to the next. This fuses all steps into a single pass without intermediate collections.

Common mistakes

  • Expecting the stream to run without a terminal operation.
  • Reusing a stream.
  • Side effects and shared mutable state in lambdas.
  • Using parallelStream() by default.
  • toMap with duplicate keys.

How Unravel recognises it

When you paste code, Unravel looks for these signs of streams. Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Builds on this

Frequently asked questions

What is Streams?

A stream is a pipeline over a sequence of values: start from a source, chain steps such as filter and map, and finish with a terminal step such as collect. Nothing runs until the terminal step.

What are the most common mistakes with Streams?

Expecting the stream to run without a terminal operation. Reusing a stream. Side effects and shared mutable state in lambdas. Using parallelStream() by default. toMap with duplicate keys.

How can I see Streams in real code?

Paste your own Java into Unravel and it will point out where streams appears, explain how it works at the detail level you choose, and draw the flow of the code around it.