Common Stream Mistakes

Streams · lesson 15 of 42 · 4 min read

The stream traps that catch everyone, and the fix for each of them.

Open this lesson in the learning hub

Key points

  • A stream is single use. Reuse one and you get IllegalStateException. Keep the source in a variable, not the stream.
  • Do not push results into an outside list from forEach. Use collect or toList, which stay correct in parallel.
  • A chain with no terminal op does absolutely nothing, and warns you about nothing.
  • count() can skip the pipeline when the size is already known, so peek side effects may silently vanish.
  • Collectors.toMap without a merge function throws on the first duplicate key, often only in production data.
  • Do not force a stream where a loop reads better. Three chained ops is elegant; twelve is a puzzle.

Example

import java.util.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) {
        List<String> names = List.of("ada", "grace", "alan");

        // 1. A stream is single use.
        Stream<String> once = names.stream();
        System.out.println("first use  : " + once.count());
        try {
            once.count();
        } catch (IllegalStateException e) {
            System.out.println("second use : " + e.getMessage());
        }

        // 2. Collect results. Do not push them into a list from outside.
        List<String> viaForEach = new ArrayList<>();
        names.stream().filter(n -> n.length() == 4).forEach(viaForEach::add);
        System.out.println("side effect: " + viaForEach);
        System.out.println("collected  : " + names.stream().filter(n -> n.length() == 4).toList());

        // 3. No terminal op means no work happens. Ever.
        names.stream().map(n -> { System.out.println("NEVER PRINTED"); return n; });
        System.out.println("no terminal: nothing ran");

        // 4. count() can skip the pipeline when the size is already known,
        //    so peek side effects may silently vanish.
        long c = names.stream().peek(n -> System.out.println("peek " + n)).count();
        System.out.println("count      : " + c);
    }
}

Streams reward pure functions and punish shared mutable state.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Streams course, and every lesson in it is listed on the Streams contents page.