Back to Stream: reduce & Aggregate
Q6Question 6 of 9

Find the total salary of all employees using reduce()

Key Concepts
  • reduce(identity, accumulator, combiner) folds all elements into a single value
  • The identity is the starting value and the empty-stream result: BigDecimal.ZERO for addition
  • The accumulator adds each employee's salary to the running total
  • The combiner merges partial totals in parallel: BigDecimal::add
  • Always use BigDecimal for money, never double, because double accumulates rounding error
  • For a simple sum, mapToDouble(...).sum() or Collectors.summingDouble is simpler, but reduce demonstrates the fold

Definition

`reduce(ZERO, (sum, e) -> sum.add(e.salary), BigDecimal::add)` folds every salary into one grand total.
  • reduce(identity, accumulator, combiner) folds all elements into a single value.
  • Identity: BigDecimal.ZERO is the starting value and the result when the stream is empty.
  • Accumulator: (sum, e) -> sum.add(e.getSalary()) folds each employee into the running total.
  • Combiner: BigDecimal::add merges partial totals for parallel execution.
  • BigDecimal: exact arithmetic for money; double would accumulate rounding error.

Priya at the National Bank of India wants the grand total of everyone's salary. She starts with a running total of zero, then walks down the staff list adding each salary. At the end the running total is the answer.

Understanding the Concepts

What does reduce do?

reduce turns many values into one. The identity is the starting value, the accumulator folds each element in, and the combiner merges partial results when the stream runs in parallel.

How do you total salaries with reduce?

JAVA
package com.bank.hr;

import java.math.BigDecimal;
import java.util.List;

public class TotalSalary {
    public static void main(String[] args) {
        List<Employee> staff = List.of(
            new Employee("Rajesh",  "Loans",   new BigDecimal("95000")),
            new Employee("Priya",   "IT",      new BigDecimal("120000")),
            new Employee("Amit",    "Finance", new BigDecimal("85000")),
            new Employee("Kavita",  "Loans",   new BigDecimal("78000")));

        BigDecimal total = staff.stream()
            .reduce(
                BigDecimal.ZERO,
                (sum, e) -> sum.add(e.getSalary()),
                BigDecimal::add);

        System.out.println("Total salary: Rs. " + total.toPlainString());
    }
}
TEXT
Total salary: Rs. 378000

Why use the three-argument reduce?

Because an Employee is not itself a BigDecimal, you need the three-argument form. The two-argument form requires both the identity and elements to be the same type.

DIAGRAM
| Form | Signature | When to use |
|---|---|---|
| Two-argument | reduce(identity, BinaryOperator<T>) | Same type: e.g. sum of ints |
| Three-argument | reduce(identity, BiFunction<U,T,U>, BinaryOperator<U>) | Different types: Employee → BigDecimal |

Why BigDecimal and not double?

JAVA
// WRONG: double accumulates rounding error
double total = staff.stream()
    .mapToDouble(e -> e.getSalary().doubleValue())
    .sum();
// May not be exactly 378000 due to floating-point representation

// CORRECT: BigDecimal.add is exact
BigDecimal total = staff.stream()
    .reduce(BigDecimal.ZERO,
        (sum, e) -> sum.add(e.getSalary()),
        BigDecimal::add);
// Exactly 378000

What is the combiner for?

The combiner merges two partial BigDecimal totals when the stream runs in parallel. In a sequential stream the combiner is effectively unused, but supplying it keeps the code correct if someone later calls parallel().

What are common mistakes?

Mistake: using the wrong identity for addition.

JAVA
// WRONG: identity 100 inflates every result by 100
BigDecimal wrong = staff.stream()
    .reduce(new BigDecimal("100"),
        (sum, e) -> sum.add(e.getSalary()),
        BigDecimal::add);
// 378100, not 378000!

// CORRECT: identity for addition is ZERO
BigDecimal correct = staff.stream()
    .reduce(BigDecimal.ZERO,
        (sum, e) -> sum.add(e.getSalary()),
        BigDecimal::add);

What about an empty employee list?

With the identity form, the result for an empty stream is the identity itself: BigDecimal.ZERO. This is correct: the total salary when no one is on the payroll is zero.

What is the simpler alternative for a plain sum?

For a simple numeric sum, map to BigDecimal and reduce with the two-argument form is cleaner.

JAVA
// Two-step: map then reduce
BigDecimal total = staff.stream()
    .map(Employee::getSalary)
    .reduce(BigDecimal.ZERO, BigDecimal::add);

Or with Collectors.reducing:

JAVA
import java.util.stream.Collectors;

BigDecimal total = staff.stream()
    .map(Employee::getSalary)
    .collect(Collectors.reducing(BigDecimal.ZERO, BigDecimal::add));

What should a senior engineer know?

  • The identity must be a true identity for the operation: ZERO + x == x.
  • The accumulator must be associative for parallel correctness.
  • For a plain numeric sum, mapToDouble(...).sum() or the Collectors approach is simpler; reduce is for demonstrating the fold.
  • In production, consider Collectors.reducing as a downstream of groupingBy for per-department totals.

What are common interview follow-up questions?

  • "Why not use double for money?" double is imprecise.
  • "What is the identity for multiplication?" 1 (BigDecimal.ONE).
  • "What happens with an empty stream?" The identity is returned.

Visual Overview

DIAGRAM
  reduce(ZERO, (sum, e) -> sum.add(e.salary), BigDecimal::add)

  Step 1:  ZERO + 95000  = 95000
  Step 2:  95000 + 120000 = 215000
  Step 3:  215000 + 85000 = 300000
  Step 4:  300000 + 78000 = 378000

  Result: BigDecimal("378000")

  Empty stream → returns identity: BigDecimal.ZERO

Key Takeaway

To total salaries with reduce I use the three-argument form because an Employee is not itself a number. The identity is BigDecimal.ZERO, the accumulator adds each salary to the running total, and the combiner merges partial totals for parallel execution.

I always use BigDecimal for money, never double, because double accumulates rounding error over many values. For an empty stream the identity form returns ZERO, which is the correct total when no one is on the payroll.

In my banking project at the National Bank of India this computed the total monthly salary outflow. For a plain numeric sum, the two-step approach of map(getSalary).reduce(ZERO, BigDecimal::add) is simpler, but the three-argument form demonstrates the fold.

Remember one sentence: reduce(ZERO, (sum, e) -> sum.add(salary), BigDecimal::add) folds all salaries into one exact BigDecimal total.

---

Share
Use ← → arrow keys to navigate