Back to Stream Creation & Utilities
Q13Question 13 of 18

Generate an infinite stream of random salaries using Stream.generate(Math::random) with limit(10)

Key Concepts
  • Stream.generate(Supplier) creates an infinite stream by repeatedly calling the supplier
  • limit(n) bounds the infinite stream to exactly n elements, making a terminal operation safe
  • Without limit or another short-circuiting operation, the pipeline would run forever
  • generate produces unordered, independent values: no notion of position or sequence
  • For reproducible randomness in tests, use a seeded Random instead of Math.random()
  • Stream.generate is for stateless independent values; Stream.iterate is for sequences depending on the previous value

Definition

`Stream.generate(supplier).limit(10)` takes ten values from an otherwise infinite stream by bounding it with `limit`.
  • Stream.generate(Supplier) creates an infinite, unordered stream by repeatedly calling the supplier.
  • limit(n) is a short-circuiting operation that stops the stream after n elements.
  • Without limit, any terminal operation would run forever.
  • Unordered nature: the supplier has no notion of position; values are independent.
  • For reproducible tests, use a seeded Random instead of Math.random().

Priya at National Bank of India has a machine that prints a random salary amount on demand, forever. She presses the button but tells it to stop after ten prints, giving her exactly ten synthetic salary values for a load test.

Understanding the Concepts

What is Stream.generate?

Stream.generate(Supplier) creates an infinite stream by calling the supplied function each time an element is demanded. The stream never ends on its own: it keeps calling the supplier for as long as elements are requested. You must bound it with a short-circuiting operation like limit(n) before any terminal operation.

How do you generate random salaries?

JAVA
package com.bank.hr;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Random;
import java.util.stream.Stream;

public class RandomSalaryGenerator {

    public static void main(String[] args) {
        // Generate 10 random salaries between 500,000 and 2,000,000
        Random rng = new Random(42);  // seeded for reproducibility

        List<BigDecimal> salaries = Stream.generate(
                () -> BigDecimal.valueOf(500_000 + rng.nextInt(1_500_001))
                    .setScale(2, RoundingMode.HALF_UP))
            .limit(10)
            .toList();

        salaries.forEach(s -> System.out.println("Rs." + s));
    }
}
TEXT
Rs.1630289.00
Rs.1539046.00
Rs.689702.00
Rs.1485995.00
Rs.1230290.00
Rs.867810.00
Rs.1953402.00
Rs.1108204.00
Rs.1365866.00
Rs.529511.00

Why is limit mandatory for infinite streams?

Without limit or another short-circuiting operation (findFirst, anyMatch), the stream would call the supplier indefinitely when a terminal operation pulls elements. The program would never finish.

JAVA
// WRONG: no limit on an infinite stream
Stream.generate(Math::random)
    .forEach(System.out::println);  // prints forever, never terminates

// CORRECT: bound with limit
Stream.generate(Math::random)
    .limit(10)
    .forEach(System.out::println);  // prints exactly 10 values

How is generate different from iterate?

| Aspect | Stream.generate(supplier) | Stream.iterate(seed, f) |
|---|---|---|
| Value dependency | Independent: no relation between values | Dependent: each value from the previous |
| Ordering | Unordered | Ordered (sequential) |
| Use case | Random values, constant values | Sequences (Fibonacci, powers, counters) |
| Parallel safety | Supplier must be thread-safe | Safe if function is pure |

What are common mistakes?

Mistake: Forgetting to bound the infinite stream.

JAVA
// WRONG: forEach on an infinite stream never terminates
Stream.generate(() -> new BigDecimal("1000000"))
    .forEach(System.out::println);  // runs forever

// CORRECT: always limit infinite streams
Stream.generate(() -> new BigDecimal("1000000"))
    .limit(5)
    .forEach(System.out::println);  // prints 5 times

What about reproducible randomness?

For unit tests, use a seeded Random so the same seed produces the same sequence on every run.

JAVA
Random seeded = new Random(12345);
List<Double> reproducible = Stream.generate(seeded::nextDouble)
    .limit(5)
    .toList();
// Same output every run with seed 12345

Math.random() uses a shared, unseeded Random and produces different values each run.

What about parallelizing a generate stream?

  • Avoid parallelizing Stream.generate because:
  • The supplier may not be thread-safe (e.g., Math.random() uses a shared Random).
  • The stream is unordered, so ordering is meaningless.
  • There is no efficient way to split an infinite supplier.

What should a senior engineer know?

Choose generate for stateless, independent values like random numbers, timestamps, or constant fill values. Choose iterate for sequences where each value depends on the previous. Always bound infinite streams with limit. Use seeded Random for reproducible tests.

What are common interview follow-up questions?

  • "What happens without limit?" The terminal operation runs forever.
  • "How do you make it reproducible?" Use a seeded Random.
  • "Can you parallelize generate?" Not recommended: supplier thread safety and no efficient splitting.
  • "What is the difference from iterate?" Generate is stateless/independent; iterate is stateful/dependent.

Visual Overview

DIAGRAM
  Stream.generate(supplier)
       │
       │  supplier() → val1   (infinite)
       │  supplier() → val2
       │  supplier() → val3
       │  ...forever...
       │
       │  limit(10)  ← bounds the stream
       ▼
  [val1, val2, val3, ..., val10]  ← exactly 10 elements
       │
       │  terminal operation (toList, forEach, etc.)
       ▼
  Result

Key Takeaway

Stream.generate takes a supplier and produces an infinite stream, calling the supplier each time an element is demanded. Because it never ends on its own, you must bound it with a short-circuiting operation like limit before any terminal operation. generate(random).limit(10) gives exactly ten synthetic values.

At the National Bank of India, Rajesh uses this to seed load tests with random salary data. He uses a seeded Random for reproducibility so the same seed generates the same test data on every run.

The key discipline is always bounding infinite streams. The choice between generate and iterate depends on whether values are independent (use generate) or sequential with each depending on the previous (use iterate). Never parallelize a generate stream because the supplier may not be thread-safe and there is no efficient way to split it.

Remember one sentence: Stream.generate(supplier).limit(n) produces n independent values from an infinite source, but always bound it with limit or the pipeline runs forever.

---

Share
Use ← → arrow keys to navigate