🌊 Streams API · Intermediate

reduce in Java

Identity, accumulator, combiner; summing and folding.

🧩 The mysteryreduce(10, Integer::sum) should mean "10 plus the sum". On a parallel stream, the answer is sometimes way bigger. Where do the extra 10s come from?

Rolling a snowball

reduce folds a stream into one value: it keeps a running result and combines it with each next element, like a snowball growing as it rolls. reduce(identity, accumulator).

int total = Stream.of(3, 4, 5)
    .reduce(0, Integer::sum);
// 0+3=3, 3+4=7, 7+5=12

The identity is the starting value

The identity is where the snowball starts, and it must be neutral: 0 for sums, 1 for products. An empty stream simply returns the identity, so Stream.<Integer>empty().reduce(0, Integer::sum) is 0.

🔮 Predict it

Product time

What does this print?

int r = Stream.of(2, 3, 5)
    .reduce(1, (a, b) -> a * b);
System.out.println(r);
  1. 10
  2. 30
  3. 0
Show the answer

30 — 1×2=2, ×3=6, ×5=30. Starting from 0 would make every product 0.

🔮 Predict it

A suspicious identity

What does this print?

int r = Stream.of(1, 2)
    .reduce(100, Integer::sum);
System.out.println(r);
  1. 3
  2. 100
  3. 103
Show the answer

103 — the identity is folded into the result as the starting point: 100 + 1 + 2.

No identity → Optional

reduce(accumulator) has no starting value, so for an empty stream there's no answer: it returns an **Optional**, empty when there were no elements.

Optional<Integer> r = Stream.<Integer>empty()
    .reduce(Integer::sum);
r.isPresent(); // false
⚠️ The trap

Fake identities break in parallel

In parallel, each chunk starts from the identity, then a combiner merges partial results (the 3-arg reduce(id, acc, comb) lets you supply it). With identity 10, ten may be added once per chunk. Use a true identity and add 10 afterwards. The accumulator must also be associative.

// wrong in parallel:
nums.parallelStream()
    .reduce(10, Integer::sum);
// right:
10 + nums.stream().reduce(0, Integer::sum);
💼 In the real world

Folding in the wild

reduce shows up for totals, products and merging objects (combine two Stats into one). Interviewers love the identity question, and the fake-identity bug is real: results that are correct on a laptop's sequential test and wrong under parallel load.

Key takeaways

  1. reduce(identity, accumulator) → a value
  2. reduce(accumulator) → Optional (stream may be empty)
  3. The accumulator must be associative to work in parallel
  4. A combiner merges partial results in reduce(id, acc, comb)
🤯 Did you know?

In other languages reduce is called *fold* or *inject* — and Google's famous MapReduce system is named after the same idea.

Practice questions

What does this print?

int product = Stream.of(1, 2, 3, 4)
    .reduce(1, (a, b) -> a * b);
System.out.println(product);
  1. 10
  2. 24
  3. 0
  4. 12
Check your answer

24. Starting at 1: 1×1=1, ×2=2, ×3=6, ×4=24. Starting at 0 would make every product 0.

What does this print?

int sum = Stream.of(1, 2, 3)
    .reduce(10, Integer::sum);
System.out.println(sum);
  1. 6
  2. 16
  3. 10
  4. 36
Check your answer

16. The identity is the starting value: 10 + 1 + 2 + 3 = 16. Because 10 is not a true identity for addition, this would give different results in parallel.

Next: collect and the Collectors recipe book — including a toMap that throws when two keys collide.