使用Java 8中的流查找每个部门工资第二高的员工

brvekthn  于 2022-09-18  发布在  Java
关注(0)|答案(1)|浏览(204)

我将Employee定义如下:

class Employee {
    private Long salary;
    private String department;

    // getters, constructor, etc.
}

有一个Employee的列表。
我正在努力为每个部门找一个薪水第二高的员工。
我找到了一种方法,使用以下代码获得第二高工资的员工:

employeeList.stream()
    .sorted(Comparator.comparingLong(Employee::getSalary).reversed())
    .skip(1).findFirst();

并能够通过使用获得各部门员工的最高工资

List<Employee> employeeList = // initializing the list

Map<Object, Object> topEmployeesByDept = employeeList.stream()
    .collect(groupingBy(
        e -> e.getDepartment(),
        collectingAndThen(
            maxBy(comparingLong(e -> e.getSalary())),
            Optional::get)
    ));

但我无法找到一种方法将这两种方法结合起来,为每个部门找到第二高工资的员工**。

bkhjykvo

bkhjykvo1#

要组合您提供的两个代码片段,您需要通过将属于同一部门的员工分组在一起来拆分数据,然后对Map到每个部门的Employee对象进行排序。
这就是它的实现方式:

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())
    ));

***注意:***尽管这种方法相对简单,但绝对不是最有效的方法。

该解决方案具有线性对数时间复杂度O(n log n),同时,它几乎可以线性时间复杂度***O(1)**完成,因为该任务非常接近搜索工资最高的员工。
此外,上面提供的解决方案无法解决多个员工的工资等于其部门最高工资的情况。在这种情况下,skip(1)将不起作用-它将是部门中工资最高的员工(而不是第二高的员工)。为了避免这个问题,我们可以使用辅助Map将一个员工与其部门内的特定薪资关联起来。
为了让员工在部门中拥有第二高的工资,我们可以维护一个大小为2PriorityQueue,它将包含两个Employee对象,这些对象具有迄今为止遇到的最高工资。
这就是它的实现方式:

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);
}

相关问题