Streams are single-use in Java
Reusing a consumed stream throws IllegalStateException.
A one-ride ticket
A Stream object can be traversed only once. One stream = one pipeline = one terminal op. After that it's consumed, and using it again throws **IllegalStateException** ("stream has already been operated upon or closed").
Ride it twice
What happens?
Stream<Integer> s = Stream.of(1, 2, 3);
System.out.println(s.anyMatch(n -> n > 2));
System.out.println(s.count());true 3Prints true, then throws IllegalStateExceptionCompile error
Show the answer
It prints true, then the second terminal op finds the stream already consumed and throws IllegalStateException. It compiles fine — the check happens at runtime.
Sneaky consumption
Stream<String> h = words.filter(w -> w.startsWith("h")); — can you still call words.count()?
Think about it, then reveal the answer
No. Chaining an op onto words links it into the new pipeline h. Touching words again — even just to count — throws IllegalStateException. Only h may be used now.
Needing two answers
Stream<Integer> s = list.stream();
long n = s.count();
int max = s.max(Integer::compare).get();Throws on the second line that uses s. There's no reset() method.
long n = list.stream().count();
int max = list.stream()
.max(Integer::compare).get();Streams are cheap to create: just ask the source for a new one.
Designing APIs
If callers need to iterate a result several times, don't return a Stream — the second caller would crash. Return a collection (List<Order>), or a **Supplier<Stream<T>>** that makes a fresh stream on demand.
Supplier<Stream<String>> names =
() -> people.stream().map(Person::name);
names.get().count(); // fresh stream
names.get().findFirst(); // another oneWhere it bites
The classic production version: a method returns a Stream, one caller logs its size, the next caller processes it — boom, IllegalStateException. Many teams have a simple rule: methods return collections, and streams stay local.
Key takeaways
- One stream object = one pipeline = one terminal op
- Reuse throws IllegalStateException
- Need it twice? Call list.stream() again
- Return collections (or a Supplier<Stream>) from APIs
Iterator has the same one-shot nature, but it doesn't throw on reuse: an exhausted iterator just keeps answering hasNext() with false.
Practice questions
What does this print?
Stream<String> s = Stream.of("a", "b");
System.out.println(s.count());
System.out.println(s.count());- 2 2
- 2 0
- Prints 2, then throws IllegalStateException
- Compile error
Check your answer
Prints 2, then throws IllegalStateException. The first count() consumes the stream. The second call finds it already operated upon and throws IllegalStateException.
You need both the count and the max of the same list. What's the best fix?
Stream<Integer> s = list.stream();
long n = s.count();
int max = s.max(Integer::compare).get();- Call list.stream() again for the second operation
- Call s.reset() before calling max
- Wrap the stream in parallel() so it can be reused
- Store the stream in a static field
Check your answer
Call list.stream() again for the second operation. Streams are cheap to create, so just make a new one from the source: list.stream().count() and list.stream().max(...). There is no reset method.