Learnitweb

Create a Map of Department to Highest-Paid Employee Using Streams

When working with a list of employees, a common requirement is to find the highest-paid employee in each department. Java Streams provide concise ways to do this using groupingBy and maxBy.

Assume the following Employee class:

public class Employee {
    private String name;
    private String department;
    private double salary;

    // Constructor, getters
    public Employee(String name, String department, double salary) {
        this.name = name;
        this.department = department;
        this.salary = salary;
    }

    public String getName() { return name; }
    public String getDepartment() { return department; }
    public double getSalary() { return salary; }
}

And a list of employees:

List<Employee> employees = Arrays.asList(
    new Employee("Alice", "HR", 50000),
    new Employee("Bob", "IT", 75000),
    new Employee("Charlie", "IT", 80000),
    new Employee("David", "HR", 60000)
);

Approach 1: Using groupingBy + maxBy (Optional Result)

Map<String, Optional<Employee>> highestPaidByDept = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::getDepartment,
        Collectors.maxBy(Comparator.comparing(Employee::getSalary))
    ));

highestPaidByDept.forEach((dept, empOpt) -> {
    empOpt.ifPresent(emp -> System.out.println(dept + ": " + emp.getName() + " - " + emp.getSalary()));
});

Explanation:

  • groupingBy(Employee::getDepartment) groups employees by department.
  • Collectors.maxBy(Comparator.comparing(Employee::getSalary)) finds the employee with the maximum salary in each department.
  • The result is a Map<String, Optional<Employee>> because some departments could potentially have no employees.

Approach 2: Using collectingAndThen to Get Employee Directly

Map<String, Employee> highestPaidByDept2 = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::getDepartment,
        Collectors.collectingAndThen(
            Collectors.maxBy(Comparator.comparing(Employee::getSalary)),
            Optional::get
        )
    ));

highestPaidByDept2.forEach((dept, emp) -> 
    System.out.println(dept + ": " + emp.getName() + " - " + emp.getSalary())
);

Explanation:

  • collectingAndThen allows post-processing the result of maxBy.
  • Optional::get unwraps the employee from the Optional.
  • Important: This approach throws an exception if a department has no employees. Ensure all groups are non-empty or handle empty optionals safely.

Summary

  • Approach 1: Safe, keeps an Optional<Employee> for each department.
  • Approach 2: Directly gives Employee, but requires non-empty groups or proper handling.
  • Both approaches use Streams + groupingBy + maxBy, which is concise and efficient for processing collections.