To combine the two code snippets, you've provided, you need to split the data by grouping employees that belong to the same department together and then sort the Employee objects mapped to each department.
That's how it might be implemented:
List<Employee> employeeList = // initializing the list
Map<String, Employee> topEmployeesByDept = employeeList.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.collectingAndThen(Collectors.toList(),
list -> list.stream()
.sorted(Comparator.comparing(Employee::getSalary))
.skip(1).findFirst().orElseThrow())
));
Note that although this approach is relatively simple, it's definitely not the most performant one.
This solution has a linear-logarithmic time complexity O(n log n), meanwhile it can be done with almost linear time complexity O(1) because this task is very close to searching an employee having the highest salary.
Also, the solution provided above doesn't tackle the case when several Employees have the salary equal to the highest salary in their department. In this case skip(1) would not work - it would an employee having the highest salary in the department (not the second highest). To avoid this issue we can use an auxiliary map to associate only one employee with a particular salary within its department.
In order to an employee having the second-highest salary in a department, we can maintain a PriorityQueue of size 2, which would contain two Employee objects with the highest salaries encountered so far.
That's how it might be implemented:
List<Employee> employeeList = List.of();
Map<String, Employee> topEmployeesByDept = employeeList.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.toMap(
Employee::getSalary,
Function.identity(),
(left, right) -> left
)
))
.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().values().stream()
.collect(Collector.of(
() -> new PriorityQueue<>(Comparator.comparing(Employee::getSalary)),
(Queue<Employee> queue, Employee next) -> tryAdd(queue, next),
(Queue<Employee> left, Queue<Employee> right) -> {
right.forEach(next -> tryAdd(left, next));
return left;
},
Queue::remove
))
));
public static void tryAdd(Queue<Employee> queue, Employee next) {
if (queue.size() == 2 && queue.element().getSalary() < next.getSalary()) queue.remove(); // if next value is greater than the smallest element in the queue and max size has been exceeded the smallest element needs to be removed from the queue
if (queue.size() < 2) queue.add(next);
}