A Stream is a traversal plan, not a reusable container; obtain a fresh stream for each terminal operation.
Java streams are single-use: create a new pipeline for each pass
Keep the source, not the spent pipeline
A report may need both a total and a list of rejected invoices. Retain the collection that owns the data, then request a new stream for each calculation. Calling a second terminal operation on the old stream is invalid, even when its source has not changed.
This matters for database-backed or file-backed sources too: a supplier can build a new pipeline, but it cannot resurrect a consumed cursor. Resource ownership still follows the source lifetime.
Use a supplier when pipeline assembly is repeated
The supplier below creates a fresh pipeline over the same in-memory list. It captures the list, so a later mutation would affect later traversals. Copy or snapshot the source if the two results must describe one fixed state. Stream fundamentals covers lazy execution.
Working program
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class InvoiceStreamPasses {
public static void main(String[] args) {
List<Integer> invoiceCents = Arrays.asList(4700, -300, 1250);
Supplier<Stream<Integer>> amounts = () -> invoiceCents.stream();
long accepted = amounts.get().filter(cents -> cents >= 0).count();
int total = amounts.get().filter(cents -> cents >= 0)
.mapToInt(Integer::intValue).sum();
System.out.println("accepted=" + accepted);
System.out.println("totalCents=" + total);
}
}Output
accepted=2
totalCents=5950Cost and ownership
Each pass is O(n) time over the list; the supplier and pipeline add little state, but a second pass repeats traversal and any mapper work. Materialize an intermediate result only when that extra memory is cheaper than recomputation or when a fixed snapshot is required.
Common Mistakes
- Do not cache a Stream field for later terminal operations.
- Do not assume a supplier makes a one-shot I/O cursor reusable.
- Do not run related calculations over a mutable source without defining snapshot semantics.
Read next
Java streams: lazy pipelines and bounded results, Java Stream.close: terminal traversal does not close every source, stream short circuit, collectors tomap duplicate key.
