reduce in Java
Identity, accumulator, combiner; summing and folding.
reduce(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=12The 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.
Product time
What does this print?
int r = Stream.of(2, 3, 5)
.reduce(1, (a, b) -> a * b);
System.out.println(r);10300
Show the answer
30 — 1×2=2, ×3=6, ×5=30. Starting from 0 would make every product 0.
A suspicious identity
What does this print?
int r = Stream.of(1, 2)
.reduce(100, Integer::sum);
System.out.println(r);3100103
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(); // falseFake 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);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
- reduce(identity, accumulator) → a value
- reduce(accumulator) → Optional (stream may be empty)
- The accumulator must be associative to work in parallel
- A combiner merges partial results in reduce(id, acc, comb)
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);- 10
- 24
- 0
- 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);- 6
- 16
- 10
- 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.