🌊 Streams API · Intermediate

Primitive streams in Java

IntStream, sum, average, boxed, mapToInt.

🧩 The mysteryYou try numbers.stream().sum() on a List<Integer>. Compile error: no such method. A stream of numbers that can't add itself up?

Boxes vs raw numbers

A Stream<Integer> carries Integer objects — numbers wrapped in boxes. **IntStream, LongStream and DoubleStream carry raw primitives**: no boxing, less memory, less garbage.

Numeric superpowers

Stream<T> can't assume T is a number, so **sum(), average() and summaryStatistics() live only on primitive streams. mapToInt** (or mapToLong, mapToDouble) switches in; **boxed()** switches back to Stream<Integer>.

int total = Stream.of("a", "bb", "ccc")
    .mapToInt(String::length) // IntStream
    .sum();                   // 6
🔮 Predict it

The average type

What does this print?

System.out.println(
    IntStream.of(2, 3).average());
  1. 2
  2. 2.5
  3. OptionalDouble[2.5]
Show the answer

OptionalDouble[2.5] — average() returns an OptionalDouble: a double (so 2.5, not 2), wrapped because there might be no average at all.

Empty streams

IntStream.empty().average() is **OptionalDouble.empty** — you can't average nothing. But IntStream.empty().sum() is simply 0.

⚠️ The trap

IntStream has no toList()

This doesn't compile: IntStream has no toList() method, because Java lists hold objects. Box the ints first with **boxed()**.

List<Integer> bad = IntStream.range(0, 3)
    .toList();          // compile error
List<Integer> ok = IntStream.range(0, 3)
    .boxed().toList();  // [0, 1, 2]

Summing a big List<Integer>

✗ Boxes every step
int s = nums.stream()
    .reduce(0, Integer::sum);

Each partial sum unboxes, adds, and re-boxes a new Integer object.

✓ Raw int math
int s = nums.stream()
    .mapToInt(Integer::intValue)
    .sum();

Adds primitive ints directly — lighter on memory and the garbage collector.

💼 In the real world

Statistics on the cheap

mapToInt(...).summaryStatistics() gives count, min, max, sum and average in one pass — handy for latency reports and analytics jobs. In hot paths over millions of numbers, avoiding boxing measurably reduces GC pressure.

Key takeaways

  1. mapToInt / mapToLong / mapToDouble → primitive stream
  2. sum() returns a number; average() returns OptionalDouble
  3. boxed() converts IntStream → Stream<Integer>
  4. IntStream has no toList(); use boxed().toList()
🤯 Did you know?

There's no CharStream, ByteStream or FloatStream — "hi".chars() actually returns an IntStream of character codes.

Practice questions

What does this print?

int total = Stream.of("a", "bb", "ccc")
    .mapToInt(String::length)
    .sum();
System.out.println(total);
  1. 3
  2. 6
  3. abbccc
  4. 123
Check your answer

6. mapToInt turns each string into its length (1, 2, 3) as an IntStream, and sum adds them: 6.

What does this print?

System.out.println(IntStream.of(1, 2).average());
System.out.println(IntStream.empty().average());
  1. 1.5 0.0
  2. 1 0
  3. OptionalDouble[1.5] OptionalDouble.empty
  4. OptionalDouble[1] null
Check your answer

OptionalDouble[1.5] OptionalDouble.empty. average always returns an OptionalDouble: it's a double (1.5, not 1) and it's empty when there are no numbers to average.

Next: a stream is a one-ride ticket. Try to ride twice and Java throws you off.