Back to Stream Practice: Employee Analysis
Q40Question 40 of 43

Find the department with the highest total salary expenditure

Key Concepts
  • groupingBy(dept, summingDouble(salary)) produces Map<String, Double> of total salary per department
  • entrySet().stream().max(comparingByValue()) finds the department with the highest total
  • summingDouble is the downstream for totals; averagingDouble is for averages
  • For exact BigDecimal totals, use groupingBy(dept, reducing(BigDecimal.ZERO, getSalary, BigDecimal::add))
  • The two-step group-then-max pattern works with any numeric downstream: counting, summing, averaging
  • To rank departments by expenditure, sort the entrySet by value descending

Definition

Group by department with summingDouble(salary) as the downstream, then find max by value on the entrySet to identify the highest-spending department.
  • groupingBy(dept, summingDouble(salary)) produces Map with total salary per department.
  • entrySet().stream().max(comparingByValue()) finds the department with the highest total.
  • BigDecimal precision: use reducing(BigDecimal.ZERO, getSalary, BigDecimal::add) for exact totals.
  • Same two-step pattern as highest average and most employees, just with a different downstream.

The National Bank of India CFO asks: 'Which department costs us the most in salaries?' First sum all salaries per department, then find the department with the highest sum.

Understanding the Concepts

What does this question ask?

The two-step group-then-max pattern applied to total salary expenditure.

How do you find the highest total salary department?

JAVA
package com.bank.hr;

import java.util.*;
import java.util.stream.Collectors;

public class HighestTotalSalary {
    public static void main(String[] args) {
        List<Employee> employees = Employee.getSampleEmployees();

        Map<String, Double> totalByDept = employees.stream()
            .collect(Collectors.groupingBy(
                Employee::getDepartment,
                Collectors.summingDouble(
                    e -> e.getSalary().doubleValue())));

        Optional<Map.Entry<String, Double>> highest = totalByDept.entrySet()
            .stream()
            .max(Map.Entry.comparingByValue());

        highest.ifPresent(e ->
            System.out.printf("Highest expenditure: %s (Rs.%.2f)%n",
                e.getKey(), e.getValue()));

        totalByDept.entrySet().stream()
            .sorted(Map.Entry.<String, Double>comparingByValue().reversed())
            .forEach(e -> System.out.printf("  %s: Rs.%.2f%n",
                e.getKey(), e.getValue()));
    }
}
TEXT
Highest expenditure: TRADING (Rs.4500000.00)
  TRADING: Rs.4500000.00
  RISK: Rs.3600000.00
  COMPLIANCE: Rs.2100000.00
  OPERATIONS: Rs.1800000.00

How do you use exact BigDecimal arithmetic?

JAVA
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 + ": Rs." + total));

How is summingDouble different from counting and averaging?

| Downstream | Result type | What it computes |
|---|---|---|
| counting() | Long | Number of elements |
| summingDouble(salary) | Double | Sum of salary values |
| averagingDouble(salary) | Double | Mean of salary values |
| summarizingDouble(salary) | DoubleSummaryStatistics | All five metrics |

What are common mistakes?

Mistake: using summingInt for BigDecimal salary.

JAVA
// WRONG: summingInt expects ToIntFunction, not BigDecimal
Collectors.summingInt(Employee::getSalary); // compile error

// CORRECT: summingDouble with conversion
Collectors.summingDouble(e -> e.getSalary().doubleValue());

// BEST: reducing with BigDecimal for exact totals
Collectors.reducing(BigDecimal.ZERO, Employee::getSalary, BigDecimal::add);

What about computing budget percentage per department?

JAVA
double grandTotal = totalByDept.values().stream()
    .mapToDouble(Double::doubleValue).sum();

totalByDept.forEach((dept, total) ->
    System.out.printf("%s: Rs.%,.0f (%.1f%%)%n",
        dept, total, (total / grandTotal) * 100));

What about filtering departments above a threshold?

JAVA
BigDecimal threshold = new BigDecimal("2000000");

List<String> highSpending = exactTotals.entrySet().stream()
    .filter(e -> e.getValue().compareTo(threshold) > 0)
    .map(Map.Entry::getKey)
    .toList();

What are common interview follow-up questions?

DIAGRAM
- "How do you find the department with the lowest expenditure?" Replace max with min.
- "How do you compute percentage of total budget per department?" First compute grand total, then divide each department total by it.
- "How do you exclude departments under a threshold?" Filter the result map: `filter(e -> e.getValue() > threshold)`.

Visual Overview

DIAGRAM
  Step 1: groupingBy(dept, summingDouble(salary))
  ┌────────────────────────────────────┐
  │  TRADING     → Rs.4,500,000       │
  │  RISK        → Rs.3,600,000       │
  │  COMPLIANCE  → Rs.2,100,000       │
  │  OPERATIONS  → Rs.1,800,000       │
  └────────────────────────────────────┘

  Step 2: max(comparingByValue())
  → TRADING (Rs.4,500,000) ← highest

Key Takeaway

Finding the department with the highest total salary uses the same two-step pattern: group with summingDouble, then max on the entrySet. For exact monetary totals, use Collectors.reducing(BigDecimal.ZERO, Employee::getSalary, BigDecimal::add) instead of summingDouble.

The two-step aggregate-then-max pattern is versatile: swap the downstream (counting, summing, averaging, summarizing) to answer different business questions with the same structural approach.

Remember one sentence: groupingBy with summingDouble gives total salary per department, then max on the entrySet identifies the highest-spending department.

---

Share
Use ← → arrow keys to navigate