Course contents

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.

PremiumGood to knowLesson16 min readCoreReviewed Oct 2026 · Java 21
On this page
  1. Picture it first: conveyor belt versus ticket machine
  2. iterate under the hood: a previous value plus a next function
  3. Java 9 iterate: a stream-shaped for loop
  4. Primitive infinite streams and random values
  5. generate: supplier pulls and unordered streams
  6. How infinite streams stop, and how they hang
  7. 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)

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) and IntStream.iterate
  • Use the Java 9 **three-argument iterate** form as a stream-shaped for loop
  • Explain why Stream.generate(Supplier) is unordered and where Random.ints() fits
  • Make unbounded streams finite with limit, takeWhile and short-circuiting terminals
  • Spot hanging pipelines such as sorted(), distinct() and collect() on infinite streams
  • Avoid parallel generate traps with stateful suppliers and late limit operations

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.

Fig. 1

Stream iterate builds each value from the previous value while generate asks an independent supplier for the next value

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 reason toList() can finish.
  • The sequence is ordered, so skip, limit, takeWhile and findFirst have 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.

In the real world

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

Practise this topicOperations with streams: interview questions & scenarios
Further reading (5)