🌊 Streams API · Intermediate

Short-circuiting & infinite streams in Java

limit and findFirst make infinite streams finite.

🧩 The mysteryStream.generate(() -> 1).count() — will it return a huge number, crash, or never finish? Infinite streams are powerful… if you know the off switch.

Rivers that never end

Stream.iterate(seed, f) and Stream.generate(supplier) are infinite. Laziness makes that fine — values are produced only when pulled — as long as something stops the flow.

The stoppers

Short-circuiting ops can finish without seeing everything: **limit(n), takeWhile(p), findFirst, anyMatch… Ops that need every** element — count(), sorted(), toList() — never finish on an infinite stream. And **filter can't stop anything**: it never knows whether more matches will come.

🔮 Predict it

Taking five

What does this print?

var r = Stream.iterate(1, n -> n * 3)
    .limit(4)
    .toList();
System.out.println(r);
  1. [3, 9, 27, 81]
  2. [1, 3, 9, 27]
  3. [1, 3, 9]
Show the answer

[1, 3, 9, 27] — iterate emits the seed first, then keeps tripling; limit(4) keeps four values.

🔮 Predict it

Searching forever?

What does this print?

boolean found = Stream.iterate(1, n -> n + 1)
    .anyMatch(n -> n * n > 30);
System.out.println(found);
  1. true
  2. false
  3. It never finishes
Show the answer

true — at n = 6, 36 > 30, and anyMatch short-circuits right there. The infinite source is no problem.

takeWhile vs filter

**takeWhile(p) (Java 9) keeps elements until the first one that fails**, then stops completely. filter checks every element and keeps going.

Stream.of(2, 4, 5, 6)
    .takeWhile(n -> n % 2 == 0); // [2, 4]
Stream.of(2, 4, 5, 6)
    .filter(n -> n % 2 == 0);    // [2, 4, 6]
⚠️ The trap

filter can't end infinity

This never finishes: filter(n -> n < 10) drops big numbers, but it can't know none will ever match again, so toList waits forever. Swap it for **takeWhile(n -> n < 10)**.

Stream.iterate(0, n -> n + 1)
    .filter(n -> n % 2 == 0)
    .filter(n -> n < 10)   // never stops!
    .toList();
💼 In the real world

Infinite on purpose

Infinite streams model ID generators, retry back-offs (iterate(100, d -> d * 2)), and paginated APIs ("fetch next page until empty" with the 3-arg iterate or takeWhile). A missing stopper shows up as a hung thread and a CPU stuck at 100%.

Key takeaways

  1. limit(n) and takeWhile(p) cut an infinite stream short
  2. findFirst/anyMatch stop as soon as they have an answer
  3. filter can't stop a stream: it never knows if more will match
  4. count(), sorted() or toList() on an infinite stream never end
🤯 Did you know?

Stream.iterate(1, n -> n + 1) doesn't stop at Integer.MAX_VALUE — it silently overflows to negative numbers and keeps going.

Practice questions

What does this print?

List<Integer> r = Stream.iterate(2, n -> n * 2)
    .limit(5)
    .toList();
System.out.println(r);
  1. [2, 4, 8, 16]
  2. [2, 4, 8, 16, 32]
  3. [4, 8, 16, 32, 64]
  4. [1, 2, 4, 8, 16]
Check your answer

[2, 4, 8, 16, 32]. iterate starts at the seed 2 and keeps doubling; limit(5) keeps the first five values.

What does this print?

int first = Stream.iterate(1, n -> n + 1)
    .filter(n -> n * n > 50)
    .findFirst()
    .get();
System.out.println(first);
  1. 7
  2. 8
  3. 50
  4. It never finishes
Check your answer

8. 7×7 = 49 is not > 50, but 8×8 = 64 is. findFirst stops pulling right there, so the infinite source is no problem.

Next: the debugging tool peek, and why it sometimes doesn't run at all.