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:
collectingAndThenallows post-processing the result ofmaxBy.Optional::getunwraps the employee from theOptional.- 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.
