Definition
- Step 1:
groupingBy(dept, summingDouble(salary))produces total salary per department. - Step 2:
max(comparingByValue())on the entrySet identifies the highest-spending department. - Step 3:
filter(dept == maxDept).max(comparing(salary))finds the top earner in that department. - Three-step derivation: each step's result feeds into the next.
- Clarity over brevity: separate named variables are easier to read and debug than a single chained expression.
The CFO at National Bank of India asks: 'In the department that costs us the most in total salaries, who is the highest-paid person?' Rajesh first totals each department's payroll, finds the costliest department, then looks up who earns the most there.
Understanding the Concepts
What does this three-step derivation involve?
This problem chains multiple aggregations: the output of one feeds the input of the next. Step 1 computes a departmental total, Step 2 identifies the maximum among those totals, and Step 3 drills into the winning department to find its top earner. Each step is a distinct stream operation.
How do you implement it with streams?
package com.bank.hr;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
public class TopEarnerInMaxDept {
record Employee(String id, String name, String department, BigDecimal salary) {}
public static void main(String[] args) {
List<Employee> staff = List.of(
new Employee("SAV2001", "Rajesh", "TRADING", new BigDecimal("1800000")),
new Employee("SAV2002", "Priya", "TRADING", new BigDecimal("1200000")),
new Employee("SAV2003", "Meera", "TRADING", new BigDecimal("1500000")),
new Employee("SAV2004", "Deepak", "RISK", new BigDecimal("1500000")),
new Employee("SAV2005", "Amit", "RISK", new BigDecimal("900000")),
new Employee("SAV2006", "Vikram", "COMPLIANCE", new BigDecimal("1300000")),
new Employee("SAV2007", "Kavita", "OPERATIONS", new BigDecimal("700000"))
);
// Step 1: Total salary per department
Map<String, Double> totalByDept = staff.stream()
.collect(Collectors.groupingBy(
Employee::department,
Collectors.summingDouble(e -> e.salary().doubleValue())));
// Step 2: Department with highest total
String maxDept = totalByDept.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElseThrow(() -> new NoSuchElementException("No departments found"));
// Step 3: Top earner in that department
Employee topEarner = staff.stream()
.filter(e -> e.department().equals(maxDept))
.max(Comparator.comparing(Employee::salary))
.orElseThrow();
System.out.printf("Highest-spending dept: %s (Rs.%.0f total)%n",
maxDept, totalByDept.get(maxDept));
System.out.printf("Top earner: %s (Rs.%s)%n",
topEarner.name(), topEarner.salary());
}
}
Highest-spending dept: TRADING (Rs.4500000 total)
Top earner: Rajesh (Rs.1800000)
Can you combine all three steps into one expression?
Yes, using collectingAndThen, but it hurts readability.
Optional<Employee> oneLiner = staff.stream()
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(Employee::department,
Collectors.summingDouble(e -> e.salary().doubleValue())),
map -> map.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)))
.flatMap(dept -> staff.stream()
.filter(e -> e.department().equals(dept))
.max(Comparator.comparing(Employee::salary)));
The three-step version with named variables is far clearer for interviews and production code.
How is highest total different from highest average?
| Metric | Collector | Result |
|---|---|---|
| Highest total | summingDouble(salary) | Department spending the most overall |
| Highest average | averagingDouble(salary) | Department paying the most per person |
A large department with moderate salaries may have the highest total, while a small department with a few highly paid experts has the highest average.
What are common mistakes?
Mistake: Confusing highest average with highest total.
// WRONG if the question asks for highest total
Collectors.averagingDouble(e -> e.salary().doubleValue()) // computes average
// CORRECT for highest total
Collectors.summingDouble(e -> e.salary().doubleValue()) // computes sum
What about edge cases?
- Empty list:
orElseThrow()throwsNoSuchElementException. Handle with a default or a meaningful error. - Tie in total:
maxreturns one arbitrarily. To handle ties, filter for all departments whose total equals the max. - Single employee in the winning department: That employee is trivially the top earner.
What about finding the bottom earner in the cheapest department?
Replace max with min in both steps.
String minDept = totalByDept.entrySet().stream()
.min(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElseThrow();
Employee bottomEarner = staff.stream()
.filter(e -> e.department().equals(minDept))
.min(Comparator.comparing(Employee::salary))
.orElseThrow();
What are common interview follow-up questions?
- "How do you find the bottom earner in the cheapest department?" Replace
maxwithminin both steps. - "How do you return the top 3 earners in that department?" Change Step 3 to
sorted(desc).limit(3). - "Can this be done in one pass?" Not cleanly, because Step 2 depends on Step 1's complete result.
Visual Overview
Step 1: Total salary per department
┌──────────────────────────────────────┐
│ TRADING = 1.8M + 1.2M + 1.5M │
│ = Rs.4,500,000 │
│ RISK = 1.5M + 0.9M │
│ = Rs.2,400,000 │
│ COMPLIANCE = Rs.1,300,000 │
│ OPERATIONS = Rs.700,000 │
└──────────────────────────────────────┘
│
│ Step 2: max by value
▼
TRADING (Rs.4,500,000) ← winner
│
│ Step 3: max salary in TRADING
▼
[Rajesh 1.8M, Priya 1.2M, Meera 1.5M]
→ max(salary) = Rajesh (Rs.1,800,000)
Key Takeaway
Finding the top earner in the highest-spending department is a three-step derivation: sum per department, find the max department, then find the max salary within that department. Each step's result feeds into the next.
At the National Bank of India, the CFO uses this pattern to identify the costliest department and its highest-paid employee. Rajesh keeps each step as a separate named variable for clarity rather than chaining everything into one expression, which makes the code easy to read, debug, and explain in an interview.
This pattern appears in complex interview questions where the answer requires chaining multiple aggregations. The key insight is that each aggregation must complete before the next can begin, which is why the three-step approach with named variables is both correct and clear.
Remember one sentence: sum by department, find the max department, then find the max salary within that department, keeping each step as a separate readable variable.
---