Definition
- Collectors.groupingBy(classifier, downstream) partitions elements by a classifier and applies a downstream collector to each group.
- Collectors.summingDouble(valueMapper) sums a double field within each group.
- The result is
Mapwith each value being the total salary for that department. - Matching collectors summingInt and summingLong exist for integer and long fields.
- For exact money, use reducing(BigDecimal.ZERO, Employee::getSalary, BigDecimal::add) as the downstream.
Priya at the National Bank of India sorts salary slips into department trays, then adds up the amounts in each tray. The tray label is the department and the total written on it is the department's combined pay.
Understanding the Concepts
How do you total salary per department?
groupingBy classifies employees by department, and summingDouble adds up the salaries within each group, yielding a map of department to total pay.
package com.bank.hr;
import java.util.*;
import java.util.stream.Collectors;
public class SalaryPerDept {
public static void main(String[] args) {
List<Employee> employees = List.of(
new Employee("Rajesh", "Loans", 75000.00),
new Employee("Priya", "IT", 82000.00),
new Employee("Amit", "Loans", 68000.00),
new Employee("Kavita", "IT", 91000.00),
new Employee("Neha", "Finance", 78000.00)
);
Map<String, Double> totalPerDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.summingDouble(
Employee::getSalaryAsDouble)));
totalPerDept.forEach((dept, total) ->
System.out.printf("%s: %.2f%n", dept, total));
}
}
Loans: 143000.00
IT: 173000.00
Finance: 78000.00
How is summingDouble different from other summing collectors?
| Collector | Input type | Return type | Use case |
|---|---|---|---|
| summingDouble(ToDoubleFunction) | double | Double | Salaries, percentages |
| summingInt(ToIntFunction) | int | Integer | Counts, quantities |
| summingLong(ToLongFunction) | long | Long | Large counts, timestamps |
// Count total years of experience per department
Map<String, Integer> totalExperience = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.summingInt(Employee::getYearsOfExperience)));
When should you use BigDecimal instead of summingDouble?
summingDouble uses double arithmetic, which can accumulate tiny rounding errors. For authoritative payroll figures, use a reducing collector with BigDecimal.
import java.math.BigDecimal;
Map<String, BigDecimal> exactTotals = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.reducing(
BigDecimal.ZERO,
Employee::getSalary,
BigDecimal::add)));
exactTotals.forEach((dept, total) ->
System.out.println(dept + ": " + total));
How do you sort departments by total salary?
import java.util.LinkedHashMap;
Map<String, Double> sorted = totalPerDept.entrySet().stream()
.sorted(Map.Entry.<String, Double>comparingByValue().reversed())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(a, b) -> a,
LinkedHashMap::new));
sorted.forEach((dept, total) ->
System.out.printf("%s: %.2f%n", dept, total));
IT: 173000.00
Loans: 143000.00
Finance: 78000.00
What are common mistakes?
Mistake: using summingDouble when the field returns BigDecimal, not double.
// WRONG: summingDouble expects a ToDoubleFunction, not BigDecimal
// This fails at compile time or loses precision at runtime
// CORRECT: use reducing for BigDecimal
Collectors.reducing(BigDecimal.ZERO, Employee::getSalary, BigDecimal::add)
How do you combine total with count (average)?
Use averagingDouble for per-department averages, or summarizingDouble for all five statistics.
// Average salary per department
Map<String, Double> avgPerDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.averagingDouble(Employee::getSalaryAsDouble)));
// Full statistics per department
Map<String, DoubleSummaryStatistics> statsPerDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.summarizingDouble(Employee::getSalaryAsDouble)));
What should a senior engineer know?
- summingDouble runs in a single pass and is parallel-safe because addition is associative.
- For authoritative payroll totals, always use BigDecimal with a reducing collector.
- The choice between summingDouble and BigDecimal reducing is a precision decision: if the number is displayed, summingDouble is fine; if it moves or reconciles real money, use BigDecimal.
- You can nest groupingBy for multi-level totals, such as total per department per city.
What are common interview follow-up questions?
- "How do you find the department with the highest total salary?" Stream the result and use max(comparingByValue()).
- "How do you compute total salary per department per city?" Use nested groupingBy.
- "What is the time complexity?" O(n) for a single pass over n employees.
Visual Overview
Stream<Employee>
[Rajesh:Loans:75K, Priya:IT:82K, Amit:Loans:68K, Kavita:IT:91K, Neha:Finance:78K]
│
│ groupingBy(dept, summingDouble(salary))
▼
Partitioning + Summing:
Loans → 75000 + 68000 = 143000
IT → 82000 + 91000 = 173000
Finance → 78000 = 78000
│
▼
Map<String, Double>
┌──────────────────────────┐
│ Loans → 143000.0 │
│ IT → 173000.0 │
│ Finance → 78000.0 │
└──────────────────────────┘
Key Takeaway
To total salary per department, collect with groupingBy using the department as the classifier and summingDouble over salary as the downstream. This partitions employees into groups and adds up salaries within each group in a single pass.
For dashboard figures at the National Bank of India, summingDouble is the right choice. For authoritative payroll reconciliation where every paisa matters, switch to a reducing collector with BigDecimal. The decision is simple: if the number is displayed, summingDouble is fine; if it moves real money, use BigDecimal.
summingDouble is parallel-safe because addition is associative, and it pairs with other downstream collectors like averagingDouble and summarizingDouble for richer per-group analytics.
Remember one sentence: groupingBy(dept, summingDouble(salary)) totals salaries per department in one pass, and switch to BigDecimal reducing when the number moves real money.
---