🌊 Streams API · Intermediate

Creating streams in Java

collection.stream(), Stream.of, Arrays.stream, IntStream.range, iterate, generate.

🧩 The mysteryStream.of(nums).count() on an array of three numbers returns… 1. Not 3. One. Where did the other numbers go?

Many doors into a stream

Pick the factory that matches your data: **list.stream() for collections, Arrays.stream(array) for arrays, and Stream.of(a, b, c)** for a handful of fixed values.

Stream<String> a = List.of("x", "y").stream();
IntStream b = Arrays.stream(new int[]{1, 2});
Stream<Integer> c = Stream.of(1, 2, 3);

Number ranges

IntStream.range(1, 5) gives 1, 2, 3, 4 — the end is exclusive, just like i < 5 in a for loop. IntStream.rangeClosed(1, 5) includes the end: 1 to 5.

IntStream.range(1, 5)       // 1 2 3 4
IntStream.rangeClosed(1, 5) // 1 2 3 4 5
🔮 Predict it

Range check

What does this print?

IntStream.range(2, 5)
    .forEach(System.out::print);
  1. 2345
  2. 234
  3. 345
Show the answer

234 — range stops before its end value. Use rangeClosed(2, 5) to get 2345.

Generators: streams without end

**Stream.iterate(seed, next)** applies next again and again, and **Stream.generate(supplier) calls the supplier forever. Both are infinite**, so they need limit or another stopper. Java 9 added **iterate(seed, hasNext, next), which works like a for loop and stops by itself**.

🔮 Predict it

The three-part iterate

What does this print?

List<Integer> r = Stream
    .iterate(1, n -> n < 20, n -> n * 2)
    .toList();
System.out.println(r);
  1. [1, 2, 4, 8, 16]
  2. [2, 4, 8, 16]
  3. [1, 2, 4, 8, 16, 32]
Show the answer

[1, 2, 4, 8, 16] — start at 1, continue while n < 20, double each time. 32 fails the test, so it's never emitted.

⚠️ The trap

The int[] that became one element

Stream.of(T...) is generic, and generics can't hold primitives. So an int[] can't spread into separate ints — the whole array becomes one element of a Stream<int[]>. For primitive arrays, use **Arrays.stream(arr)**, which gives an IntStream.

int[] a = {1, 2, 3};
Stream.of(a).count();     // 1: Stream<int[]>
Arrays.stream(a).count(); // 3: IntStream
💼 In the real world

Where you'll meet these

IntStream.range replaces index loops in production code and in coding interviews. Stream.generate(UUID::randomUUID).limit(n) builds test data, and the Stream.of(intArray) surprise is a classic bug in code reviews — the count is silently wrong instead of crashing.

Key takeaways

  1. list.stream() / Arrays.stream(arr) / Stream.of(a, b, c)
  2. IntStream.range(1, 5) excludes 5; rangeClosed(1, 5) includes it
  3. Stream.iterate(seed, next) and Stream.generate(supplier) can be infinite
  4. iterate(seed, hasNext, next) (Java 9) stops by itself
🤯 Did you know?

IntStream.range(0, 0) is perfectly legal: it's simply an empty stream, so count() returns 0.

Practice questions

What does this print?

IntStream.rangeClosed(1, 4)
    .forEach(System.out::print);
  1. 123
  2. 1234
  3. 01234
  4. 12345
Check your answer

1234. rangeClosed includes both ends, so it yields 1, 2, 3, 4, printed with no separator.

What does this print?

List<Integer> r = Stream
    .iterate(1, n -> n < 50, n -> n * 3)
    .toList();
System.out.println(r);
  1. [1, 3, 9, 27, 81]
  2. [3, 9, 27]
  3. [1, 3, 9, 27]
  4. [1, 3, 9, 27, 50]
Check your answer

[1, 3, 9, 27]. The three-argument iterate (Java 9) works like a for loop: start at 1, keep going while n < 50, multiply by 3. 81 fails the test, so it stops before it.

Next: we'll catch a stream being lazy — a map full of print statements that prints absolutely nothing.