Back to Stream: Predict the Output
Q2Question 2 of 7

What happens if you reuse a stream after calling a terminal operation?

Key Concepts
  • A stream can be traversed only once: it is a single-use view over a source, not a reusable data structure
  • After any terminal operation, the stream is consumed and marked used
  • Any further operation on the same stream reference throws IllegalStateException
  • The fix is to create a new stream from the original source (list.stream()) for each traversal
  • Store the source (collection or supplier), not the stream, when multiple traversals are needed
  • The single-use contract is fundamental to the lazy, pull-based stream design

Definition

Streams are single-use: reusing one after a terminal operation throws `IllegalStateException: stream has already been operated upon or closed`.
  • Single-use: a stream can be traversed only once.
  • After a terminal (collect, forEach, count), the stream is consumed.
  • Reuse throws: IllegalStateException with 'stream has already been operated upon or closed'.
  • Fix: create a new stream from the source for each traversal.
  • Store the source, not the stream, for multiple operations.

Once the conveyor belt at National Bank of India has run and emptied, Priya cannot run it again. She must set up a fresh belt from the same source.

Understanding the Concepts

What happens when you reuse a stream?

JAVA
package com.bank.core;

import java.util.List;
import java.util.stream.Stream;

public class StreamReuseDemo {

    public static void main(String[] args) {
        List<String> accounts = List.of("SAV2001", "SAV2002", "SAV2003");

        Stream<String> stream = accounts.stream();

        // First terminal: works fine
        long count = stream.count();
        System.out.println("Count: " + count);

        // Second operation on same stream: throws!
        try {
            stream.forEach(System.out::println);
        } catch (IllegalStateException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}
TEXT
Count: 3
Error: stream has already been operated upon or closed

Why are streams single-use?

Streams are pull-based: a terminal operation pulls elements through the pipeline, consuming them. After traversal, the stream's internal state is exhausted. The single-use contract enables lazy evaluation and pipeline fusion without requiring the stream to buffer all elements.

How do you process data multiple times?

JAVA
List<String> accounts = List.of("SAV2001", "SAV2002", "SAV2003");

// Create a new stream for each operation
long count = accounts.stream().count();
List<String> sorted = accounts.stream().sorted().toList();
String first = accounts.stream().findFirst().orElse("none");
// Each call to accounts.stream() creates a fresh stream

What is the difference between the source and the stream?

| Aspect | Source (Collection) | Stream |
|---|---|---|
| Reusable | Yes: call stream() many times | No: single traversal |
| Stores data | Yes: holds elements in memory | No: lazy pipeline over source |
| Modifiable | Depends on type | Never: stream ops do not modify source |

What are common mistakes?

Mistake: Storing a stream in a variable and operating on it twice.

JAVA
// WRONG: reusing the stream
Stream<String> s = accounts.stream().filter(id -> id.startsWith("SAV"));
long count = s.count();        // works
List<String> list = s.toList(); // throws IllegalStateException

// CORRECT: create fresh streams
long count = accounts.stream().filter(id -> id.startsWith("SAV")).count();
List<String> list = accounts.stream().filter(id -> id.startsWith("SAV")).toList();

What if you need to run multiple pipelines efficiently?

If re-streaming the source is expensive, materialize the intermediate result into a collection and stream that.

JAVA
List<String> filtered = accounts.stream()
    .filter(id -> id.startsWith("SAV"))
    .toList();

long count = filtered.size();
String first = filtered.getFirst();
List<String> sorted = filtered.stream().sorted().toList();

What about using a Supplier?

JAVA
import java.util.function.Supplier;

Supplier<Stream<String>> streamSupplier = () -> accounts.stream()
    .filter(id -> id.startsWith("SAV"));

long count = streamSupplier.get().count();
List<String> list = streamSupplier.get().toList();
// Each get() creates a fresh stream

What should a senior engineer know?

Never store a stream in a field or pass it around for multiple uses. Store the source or a Supplier instead. The single-use contract is fundamental to the lazy, pull-based model.

What are common interview follow-up questions?

  • "What exception is thrown?" IllegalStateException.
  • "Can you reuse the source?" Yes: list.stream() creates a new stream each time.
  • "What is a Supplier?" A factory that creates a fresh, configured stream on each call.

Visual Overview

DIAGRAM
  Stream<String> s = accounts.stream();

  s.count()          ← terminal: stream consumed
       │
       ▼
  Stream state: CONSUMED
       │
  s.forEach(...)     ← reuse attempt
       │
       ▼
  IllegalStateException:
  "stream has already been operated upon or closed"

Key Takeaway

A stream can be traversed only once. After any terminal operation consumes it, calling another operation on the same stream reference throws IllegalStateException. Streams are not reusable data structures but single-use, pull-based views over a source.

At the National Bank of India, a developer stored a stream in a variable and ran two terminals on it, hitting the exception on the second. The fix is to create a new stream from the source each time by calling list.stream() again, because the source collection is reusable even though the stream object is not.

When multiple traversals are needed, store the source or a Supplier that creates a fresh configured stream on each call. If the filtering or mapping is expensive, materialize the intermediate result into a collection and operate on that.

Remember one sentence: never reuse a stream after a terminal operation; create a new one from the source each time because streams are single-use.

---

Share
Use ← → arrow keys to navigate