Back to Stream: reduce & Aggregate
Q9Question 9 of 9

Find the product of all employee ages using reduce()

Key Concepts
  • The identity for multiplication is 1, not 0: any value times 1 is itself, and 0 collapses everything to 0
  • mapToLong(getAge).reduce(1L, (a, b) -> a * b) multiplies all ages as long primitives
  • Multiplying many values grows fast and can silently overflow a long with no exception
  • Math.multiplyExact throws ArithmeticException on overflow instead of wrapping silently
  • For truly large products, use BigInteger which has no fixed range and cannot overflow
  • The identity form always returns a value (1L for empty stream), never an Optional

Definition

`reduce(1L, (a, b) -> a * b)` multiplies all ages, and the identity must be 1, never 0.
  • Identity for multiplication: 1, because 1 * x == x and the empty product is 1.
  • mapToLong gives a LongStream for more headroom before overflow.
  • Silent overflow: long multiplication wraps around with no exception.
  • Math.multiplyExact: throws ArithmeticException on overflow instead of wrapping.
  • BigInteger: has no fixed range and cannot overflow at all.

Imagine a risk-model weight that multiplies a factor for each team member. Priya starts holding the number 1, then multiplies it by each person's age. The key is she starts at 1, not 0, because 0 would wipe everything out.

Understanding the Concepts

How do you multiply all ages with reduce?

JAVA
package com.bank.hr;

import java.util.List;

public class ProductOfAges {
    public static void main(String[] args) {
        List<Employee> staff = List.of(
            new Employee("Rajesh",  35),
            new Employee("Priya",   30),
            new Employee("Amit",    28),
            new Employee("Kavita",  32));

        long product = staff.stream()
            .mapToLong(Employee::getAge)
            .reduce(1L, (a, b) -> a * b);

        System.out.println("Product of ages: " + product);
    }
}
TEXT
Product of ages: 940800

Why is the identity 1 and not 0?

| Identity | Effect | Correct? |
|---|---|---|
| 1 | 1 * 35 * 30 * 28 * 32 = 940800 | Yes |
| 0 | 0 * 35 * 30 * 28 * 32 = 0 | No! |

The identity must satisfy identity * x == x. For multiplication that is 1. Using 0 would make every product zero.

How do you guard against overflow?

JAVA
// WRONG: silent overflow wraps to a wrong number
long product = staff.stream()
    .mapToLong(Employee::getAge)
    .reduce(1L, (a, b) -> a * b);
// If product exceeds Long.MAX_VALUE, wraps silently!

// CORRECT: Math.multiplyExact throws on overflow
long safeProduct = staff.stream()
    .mapToLong(Employee::getAge)
    .reduce(1L, Math::multiplyExact);
// Throws ArithmeticException if overflow occurs

What about BigInteger for large products?

JAVA
import java.math.BigInteger;

BigInteger product = staff.stream()
    .reduce(
        BigInteger.ONE,
        (acc, e) -> acc.multiply(BigInteger.valueOf(e.getAge())),
        BigInteger::multiply);

System.out.println("BigInteger product: " + product);
TEXT
BigInteger product: 940800

BigInteger has no fixed range and cannot overflow.

What are common mistakes?

Mistake: using 0 as the identity for multiplication.

JAVA
// WRONG: 0 collapses the entire product to 0
long wrong = staff.stream()
    .mapToLong(Employee::getAge)
    .reduce(0L, (a, b) -> a * b);
// Always 0!

// CORRECT: identity for multiplication is 1
long correct = staff.stream()
    .mapToLong(Employee::getAge)
    .reduce(1L, (a, b) -> a * b);

What about an empty stream?

With identity 1L, an empty stream returns 1L, which is the mathematical convention for the empty product.

Why does this return a plain long and not Optional?

The identity form of reduce always has a value (the identity for empty streams), so it returns a plain long, not an OptionalLong. The no-identity form would return OptionalLong.

What should a senior engineer know?

  • Multiplying many values grows exponentially; even a long can overflow for a moderate number of employees.
  • In financial models, use Math.multiplyExact to turn silent overflow into a loud exception.
  • For truly large products, use BigInteger.
  • The accumulator must be associative for parallel correctness.

What are common interview follow-up questions?

  • "Why not use 0 as the identity?" It collapses the entire product to 0.
  • "How do you detect overflow?" Use Math.multiplyExact which throws ArithmeticException.
  • "What happens with an empty stream?" Returns the identity (1).

Visual Overview

DIAGRAM
  reduce(1L, (a, b) -> a * b)

  Step 1:  1 * 35 = 35
  Step 2:  35 * 30 = 1050
  Step 3:  1050 * 28 = 29400
  Step 4:  29400 * 32 = 940800

  Result: 940800

  Overflow guard:
  Math.multiplyExact(a, b) → ArithmeticException if overflow
  BigInteger.multiply()    → no overflow possible

Key Takeaway

To multiply all ages with reduce I use identity 1, never 0, because 1 is the neutral element for multiplication. I map ages to a LongStream and reduce with multiplication, which returns a plain long because the identity form always has a value.

The critical concern is overflow. Multiplying many values grows fast and silently wraps a long to a wrong number. I guard with Math.multiplyExact, which throws ArithmeticException on overflow, or switch to BigInteger which cannot overflow at all.

In my banking project at the National Bank of India this pattern computed a composite risk weight. I used the guarded version so overflow raised an alert instead of producing a nonsense number.

Remember one sentence: identity 1 for multiplication (not 0!), and guard against overflow with Math.multiplyExact or BigInteger.

---

Share
Use ← → arrow keys to navigate