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 ofmaxBy
.Optional::get
unwraps 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.