I have an object structure like
public class Employee {
private String name;
privte String department;
private String gender;
private String designation;
private Integer salary;
}
List<Employee> listEmployees = new ArrayList<>(List.of(
new Employee("Mark", "Marketing", "male", "Sales Manager", 9200),
new Employee("Tom", "Marketing", "male", "Sales Manager", 2800),
new Employee("Travis", "Marketing", "male", "Sales Manager", 3850),
new Employee("Diana", "Marketing", "female", "Sales Manager", 1900),
new Employee("Keith", "R&D", "male", "Software Engineer", 4000),
new Employee("Liam", "R&D", "male", "Software Engineer", 5200),
new Employee("Whitney", "R&D", "female", "Software Engineer", 6000),
new Employee("Tina", "R&D", "female", "Software Engineer", 7500)
new Employee("Patrick", "Government", "female", "Service", 7100),
new Employee("Luc", "Government", "male", "Service", 3300),
new Employee("Philippe", "Government", "female", "Service", 2800),
new Employee("David", "Government", "female", "Service", 4700)
));
I want result from this employee list from each department and gender who is earning highest salary.
I tried with two filed group by department and gender after that get result map inside map then list but how to filter by highest salary and get the final result from each group?
So my result would be like below.
Employee("Mark", "Marketing", "male", "Sales Manager", 9200)
Employee("Diana", "Marketing", "female", "Sales Manager", 1900)
Employee("Liam", "R&D", "male", "Software Engineer", 5200)
Employee("Tina", "R&D", "female", "Software Engineer", 7500)
Employee("Patrick", "Government", "female", "Service", 7100)
Employee("Luc", "Government", "male", "Service", 3300)
I have tried with below code:
employeeList.stream()
.collect(Collectors.groupingBy(Employee::getDepartment, Collectors.groupingBy(Employee::getGender)));
Using groupingBy would work but is overly complex and somewhat cumbersome to get the desired results. Assuming you had the getters defined, you could put them in a map where the key
is the concatenation of the departement and gender
and the value
is the Employee
instance. Then use a BinaryOperator
as a mergeFunction
to replace the existing employee if the next employee's salary is higher. The LinkedHashMap
option is not required but is used to preserve the encounter order of the Employees. To finish, the values()
of the map are returned and put in a List
and printed.
I am using a record
for the demo.
record Employee(String getName, String getDepartment, String getGender,
String getDesignation, Integer getSalary) {
@Override
public String toString() {
return "Employee(%s, %s, %s, %s, %d)".formatted( getName, getDepartment, getGender,
getDesignation,getSalary);
}
}
Function<Employee, String> key = emp->emp.getDepartment()+emp.getGender();
BinaryOperator<Employee> mergeFunction = (max, next) ->
max.getSalary() > next.getSalary() ? max : next;
The process
List<Employee> maxSalaries = new ArrayList<>(listEmployees.stream()
.collect(Collectors.toMap(
emp->key.apply(emp), emp -> emp,
mergeFunction,
LinkedHashMap::new)).values());
maxSalaries.forEach(System.out::println);
New list of Employee:
Employee(Mark, Marketing, male, Sales Manager, 9200)
Employee(Diana, Marketing, female, Sales Manager, 1900)
Employee(Liam, R&D, male, Software Engineer, 5200)
Employee(Tina, R&D, female, Software Engineer, 7500)
Employee(Patrick, Government, female, Service, 7100)
Employee(Luc, Government, male, Service, 3300)
Here is a version that uses nested groupingBy
collectors and a reducing
collector. I still prefer the first method but this may be more in line with what you want.
Map<String, Map<String, Employee>> results2 = listEmployees.stream().collect(Collectors.groupingBy(Employee::getDepartment,
Collectors.groupingBy(Employee::getGender,
Collectors.reducing(new Employee("","","","",-1), mergeFunction ))));
results2.forEach((k,v) -> {
v.values().forEach(System.out::println);
});
prints
Employee(Tina, R&D, female, Software Engineer, 7500)
Employee(Liam, R&D, male, Software Engineer, 5200)
Employee(Patrick, Government, female, Service, 7100)
Employee(Luc, Government, male, Service, 3300)
Employee(Diana, Marketing, female, Sales Manager, 1900)
Employee(Mark, Marketing, male, Sales Manager, 9200)