Definition
- Three-argument iterate (Java 9) takes a seed, a predicate, and a next function.
- Self-terminating: the predicate stops the stream when it returns false.
- Functional for-loop: seed is the initializer, predicate is the condition, next is the increment.
- No limit needed: the predicate bounds the stream.
- For contiguous ints, prefer
IntStream.rangeClosedwhich is clearer and primitive.
Priya at National Bank of India counts account numbers from 1 to 100, adding one each time, and stops automatically the moment the number would exceed 100. It is like a for-loop expressed as a stream.
Understanding the Concepts
What is the three-argument iterate?
The Java 9 overload Stream.iterate(seed, hasNext, next) takes three arguments: a start value, a predicate that decides whether to continue producing elements, and a function that computes the next value. When the predicate returns false, the stream stops. This makes it a self-terminating alternative to the two-argument infinite iterate.
How do you use it to generate 1 to 100?
package com.bank.core;
import java.util.List;
import java.util.stream.Stream;
public class IterateWithPredicate {
public static void main(String[] args) {
// Functional for-loop: 1 to 100
List<Integer> oneToHundred = Stream.iterate(
1, // seed (initializer)
n -> n <= 100, // predicate (condition)
n -> n + 1) // next (increment)
.toList();
System.out.println("Count: " + oneToHundred.size());
System.out.println("First: " + oneToHundred.getFirst());
System.out.println("Last: " + oneToHundred.getLast());
// Banking use case: EMI schedule months
List<Integer> emiMonths = Stream.iterate(1, m -> m <= 36, m -> m + 1)
.toList();
System.out.println("EMI months (36-month loan): " + emiMonths.size());
}
}
Count: 100
First: 1
Last: 100
EMI months (36-month loan): 36
How does it compare to a for-loop?
// Traditional for-loop
for (int n = 1; n <= 100; n++) { ... }
// Three-argument iterate: same semantics as a stream
Stream.iterate(1, n -> n <= 100, n -> n + 1).forEach(n -> { ... });
// seed = initializer (int n = 1)
// predicate = condition (n <= 100)
// next = increment (n++)
How is three-argument iterate different from two-argument?
| Aspect | Two-argument iterate(seed, next) | Three-argument iterate(seed, hasNext, next) |
|---|---|---|
| Finite? | No: infinite, needs limit | Yes: predicate self-terminates |
| Java version | Java 8+ | Java 9+ |
| Use case | Count-bounded sequences | Value-bounded sequences |
| Clarity | Less clear with limit | Reads like a for-loop |
When should you prefer IntStream.rangeClosed?
For simple contiguous integer ranges, IntStream.rangeClosed(1, 100) is even clearer and avoids boxing.
// Clearer for contiguous integer ranges
java.util.stream.IntStream.rangeClosed(1, 100)
.forEach(System.out::println);
Reserve three-argument iterate for non-trivial step functions or non-contiguous sequences.
// Non-contiguous: step by 5
List<Integer> fives = Stream.iterate(5, n -> n <= 100, n -> n + 5).toList();
// [5, 10, 15, 20, ..., 100]
// Exponential: double each time until threshold
List<Long> delays = Stream.iterate(100L, d -> d <= 10_000, d -> d * 2).toList();
// [100, 200, 400, 800, 1600, 3200, 6400]
What are common mistakes?
Mistake: Using the two-argument iterate without limit for a bounded sequence.
// WRONG: two-argument iterate is infinite, needs limit
Stream.iterate(1, n -> n + 1)
.forEach(System.out::println); // runs forever
// CORRECT: use three-argument iterate with a predicate
Stream.iterate(1, n -> n <= 100, n -> n + 1)
.forEach(System.out::println); // self-terminates at 100
What about off-by-one errors?
The predicate n -> n <= 100 includes 100. If you wrote n -> n < 100, you would get 1 to 99 (exclusive end). Choose the predicate carefully based on whether you want inclusive or exclusive bounds.
What should a senior engineer know?
The three-argument iterate is the right tool when the stop condition is a value test rather than a simple count. For a plain contiguous integer range, IntStream.rangeClosed is clearer. For non-trivial steps (exponential, step-by-5, or conditional progression), three-argument iterate is the most readable option.
What are common interview follow-up questions?
- "How is this different from two-argument iterate?" Three-argument self-terminates; two-argument needs
limit. - "When should you use IntStream.rangeClosed instead?" For simple contiguous integer ranges.
- "What Java version added this?" Java 9.
- "What about off-by-one?" The predicate determines inclusive vs exclusive:
<= 100includes 100,< 100excludes it.
Visual Overview
Stream.iterate(1, n -> n <= 100, n -> n + 1) Equivalent for-loop: for (int n = 1; n <= 100; n++) ┌───────────────────────────────────────────┐ │ seed = 1 │ │ predicate(1) = true → emit 1 │ │ next(1) = 2 │ │ predicate(2) = true → emit 2 │ │ next(2) = 3 │ │ ... │ │ predicate(100) = true → emit 100 │ │ next(100) = 101 │ │ predicate(101) = false → STOP │ └───────────────────────────────────────────┘ Result: [1, 2, 3, ..., 100]
Key Takeaway
The three-argument Stream.iterate added in Java 9 takes a seed, a predicate, and a next function. It reads like a functional for-loop: the seed is the initializer, the predicate is the loop condition, and the next function is the increment. iterate(1, n -> n <= 100, n -> n + 1) generates exactly 1 through 100 and terminates itself without needing limit.
At the National Bank of India, Rajesh uses this for bounded sequences with custom steps: EMI month numbers, exponential backoff delays, or stepped thresholds. The predicate bounds the stream based on a value condition rather than a simple count, which is clearer than tacking limit onto a two-argument iterate.
For plain contiguous integer ranges, IntStream.rangeClosed is even clearer and works on primitives without boxing. Reserve three-argument iterate for non-trivial step functions or non-contiguous progressions where expressing the initializer, condition, and step as a self-terminating stream is the most readable option.
Remember one sentence: three-argument iterate(seed, hasNext, next) is a functional for-loop that self-terminates, making limit unnecessary for value-bounded sequences.
---