Infinite streams with iterate and generate
Infinite stream creation with `iterate` and `generate`, Java 9 bounded iteration, primitive and random streams, finite exits, and hanging pipeline traps.
On this page
- Picture it first: conveyor belt versus ticket machine
- iterate under the hood: a previous value plus a next function
- Java 9 iterate: a stream-shaped for loop
- Primitive infinite streams and random values
- generate: supplier pulls and unordered streams
- How infinite streams stop, and how they hang
- How to say it in the interview
♾️ What is an infinite stream?
An infinite stream is a stream whose source can keep producing elements until a downstream operation chooses to stop. Stream.iterate(1, n -> n * 2) can produce powers of two forever, so it acts like a conveyor belt with no natural end. The stream itself is lazy, but a terminal operation can still run forever if you never install a stop sign.
For this lesson, we'll focus on building unbounded and bounded sequences with Stream.iterate, the Java 9 three-argument iterate, Stream.generate, primitive infinite streams and Random.ints(). We'll also cover how to make these streams finite, where ordered and unordered behavior matters, and why some innocent-looking operations never return. For slicing mechanics, see Slicing streams: skip, limit, takeWhile and dropWhile; for short-circuiting terminals, see findFirst vs findAny and short-circuiting.
Below the line (out of scope)
- Stream laziness and pipeline fusion: How a stream pipeline executes
- Operation categories and one-shot streams: Intermediate vs terminal operations
- Primitive stream basics and boxing: Primitive streams: IntStream, LongStream and DoubleStream
- Parallel stream tuning: Parallel streams: when they help and hurt
The 30-second answer
Stream.iterate(seed, next) creates an ordered infinite sequence by repeatedly applying next to the previous value. Since Java 9, Stream.iterate(seed, hasNext, next) gives you a stream-shaped for loop that stops before emitting the first value that fails hasNext. Stream.generate(supplier) asks a supplier for each next element and is unordered, so it is good for independent values like UUIDs or random samples, not for sequences that depend on the previous value. Make infinite streams finite with limit, takeWhile or short-circuiting terminals like findFirst and anyMatch. Be careful with sorted, distinct, collect and unbounded count: they may need the whole stream, which means they never finish.
What you’ll learn
- Build ordered infinite sequences with
Stream.iterate(seed, next)andIntStream.iterate - Use the Java 9 **three-argument
iterate** form as a stream-shapedforloop - Explain why
Stream.generate(Supplier)is unordered and whereRandom.ints()fits - Make unbounded streams finite with
limit,takeWhileand short-circuiting terminals - Spot hanging pipelines such as
sorted(),distinct()andcollect()on infinite streams - Avoid parallel
generatetraps with stateful suppliers and latelimitoperations
01Picture it first: conveyor belt versus ticket machine
Picture two machines. iterate is a conveyor belt where each box is built from the previous box: 1, 2, 4, 8. generate is a ticket machine: each pull asks the supplier for another ticket, and the stream API does not promise a meaningful order between pulls.
That difference is the whole lesson. Use iterate when the next value depends on the last value; use generate when each value can be produced independently. Then add a stop condition close enough that the reader can see it.
Stream iterate builds each value from the previous value while generate asks an independent supplier for the next value
02iterate under the hood: a previous value plus a next function
Stream.iterate(seed, next) starts with the seed, emits it, then stores the result of next(seed) as the next value. It is ordered because there is a clear predecessor for every value after the seed. That makes it a natural fit for counters, schedules, IDs and recurrence-style sequences.
Try it yourself first
Before reading the output, write the first five values. Then ask yourself what would happen if the limit(5) line disappeared.
import java.util.List;
import java.util.stream.Stream;
final class BackoffScheduleDemo {
public static void main(String[] args) {
List<Integer> delaysMs = Stream.iterate(100, delay -> delay * 2)
.limit(5)
.toList();
System.out.println(delaysMs);
}
}
The output is:
[100, 200, 400, 800, 1600]
Notice three things.
- The seed,
100, is emitted as the first element. limit(5)is not decoration. It is the reasontoList()can finish.- The sequence is ordered, so
skip,limit,takeWhileandfindFirsthave prefix semantics.
Here is how I'd write a slightly more realistic retry schedule. A retry loop might cap delay at 5 seconds and stop after 6 attempts because beyond that you are usually hiding an incident, not improving reliability.
import java.util.List;
import java.util.stream.Stream;
record RetryDelay(int attempt, int millis) { }
final class RetryBackoffDemo {
public static void main(String[] args) {
List<RetryDelay> plan = Stream.iterate(
new RetryDelay(1, 100),
retry -> new RetryDelay(retry.attempt() + 1, Math.min(retry.millis() * 2, 5_000)))
.limit(6)
.toList();
System.out.println(plan);
}
}
The first version is a teaching example. This one is closer to production because it names the attempt and caps the delay. In real retry code, you would also add jitter so 1,000 clients do not retry at exactly the same millisecond.
I once reviewed a retry helper that used Stream.iterate nicely, then called collect(toList()) without a limit because the caller was supposed to add one later. The code passed unit tests with a mocked source and then hung a worker thread in staging. The fix was not clever: put the cap where the unbounded source is created, so nobody can forget it downstream.
Premium lesson
Keep reading with the Java Pass
The rest of this lesson is part of Java One-Stop Interview Prep: in-depth explanations, diagrams, model answers and how to say them in the interview.
- Java 9 iterate: a stream-shaped for loop
- Primitive infinite streams and random values
- generate: supplier pulls and unordered streams
- How infinite streams stop, and how they hang
- How to say it in the interview
From ₹199 · one-time payment · No auto-renewal