java - 如何在 Java Stream 中使用 MaxBy 进行分组和聚合
问题描述
我有一个Employee
对象列表如下。
public class Employee {
int eno;
String name;
String dept;
int salary;
......
}
要求是获取每个部门获得最高薪水的员工姓名列表。部门中可以有不止一名员工具有相同的最高薪水。例如,销售团队可能有 2 名员工,比如 Alex 和 John,他们的薪水最高。
List<Employee> eList = new ArrayList<>();
eList .add(new Employee(101, "ALEX","SALES",7000));
eList.add(new Employee(102, "KATHY","ADMIN",3000));
eList.add(new Employee(103, "JOHN","SALES",7000));
eList.add(new Employee(104, "KONG","IT",5000));
eList.add(new Employee(105, "MIKE","SALES",4000));
预期的输出是 -> Map(DEPT, List Of Names with max payment)
使用下面的代码,我只能得到一个 Employee 对象作为输出,即使有多个员工满足条件。
Map<String, Employee> maxSalEmpNameByDept = eList.stream().collect(groupingBy(Employee::getDept,
collectingAndThen(maxBy(Comparator.comparing(Employee::getSalary)), Optional::get)));
这是因为 MaxBy 方法在找到满足给定条件的第一个项目时停止。
我的问题是:
- 我们如何获得
Map <String, List<Employee>>
每个部门的最高薪员工 ( ) 的列表? - 如何仅检索
Map <String, List<String>>
每个部门中薪水最高的员工姓名列表( )?
解决方案
这是一种不使用显式Comparator
或第三方库的方法。我填写了您的 Employee 类并添加了一些额外的员工。
提供的数据
List<Employee> eList = new ArrayList<>();
eList.add(new Employee(101, "ALEX", "SALES", 7000));
eList.add(new Employee(102, "KATHY", "ADMIN", 3000));
eList.add(new Employee(103, "JOHN", "SALES", 7000));
eList.add(new Employee(104, "KONG", "IT", 5000));
eList.add(new Employee(106, "MIKE", "SALES", 4000));
eList.add(new Employee(107, "BOB", "IT", 4000));
eList.add(new Employee(108, "SARAH", "ADMIN", 2000));
eList.add(new Employee(109, "RALPH", "SALES", 4000));
eList.add(new Employee(110, "JUNE", "ADMIN", 3000));
以下是每个部门的最高工资。
- 首先,按部门对第一张地图进行分组。
- 现在创建另一个地图并按员工的薪水对员工进行分组。工资是这张地图的关键。该值将是员工列表。
- 由于内部映射是 a
Treemap
,因此值自然从低到高排序,因此最后一个条目的薪水最高。 - 只需检索每个部门的最后一个条目,您就会拥有最高薪的员工。
Map<String, List<Employee>> highestSalaries = eList.stream()
.collect(Collectors.groupingBy(Employee::getDept,
Collectors.groupingBy(Employee::getSalary,
TreeMap::new, Collectors.toList())))
.entrySet().stream()
.collect(Collectors.toMap(e -> e.getKey(),
e -> e.getValue().lastEntry().getValue()));
highestSalaries.entrySet().forEach(System.out::println);
印刷
ADMIN=[{102, KATHY, ADMIN, 3000}, {110, JUNE, ADMIN, 3000}]
IT=[{104, KONG, IT, 5000}]
SALES=[{101, ALEX, SALES, 7000}, {103, JOHN, SALES, 7000}]
要仅获取名称,请使用刚刚创建的地图
- 流式传输值(这是员工列表)
- 然后通过 flatMap 将员工列表放在一个
Employee
流中 - 请记住,此时这些是每个部门的最高薪水,因此只需按部门对员工进行分组并在列表中收集他们的姓名。
Map<String, List<String>> highestSalariesNames =
highestSalaries.values().stream()
.flatMap(List::stream)
.collect(Collectors.groupingBy(
Employee::getDept,
Collectors.mapping(Employee::getName,
Collectors.toList())));
highestSalariesNames.entrySet().forEach(System.out::println);
印刷
IT=[KONG]
ADMIN=[KATHY, JUNE]
SALES=[ALEX, JOHN]
我用构造函数、getter 和 toString 修改了你的类。
public static class Employee {
private int eno;
private String name;
private String dept;
private int salary;
public Employee(int eno, String name, String dept,
int salary) {
this.eno = eno;
this.name = name;
this.dept = dept;
this.salary = salary;
}
public int getEno() {
return eno;
}
public String getName() {
return name;
}
public String getDept() {
return dept;
}
public int getSalary() {
return salary;
}
@Override
public String toString() {
return String.format("{%s, %s, %s, %s}", eno, name,
dept, salary);
}
}
}
推荐阅读
- git - 数据库端更改 Git 分支之间的合并问题
- css - 角度 9:ngFor 内 ngClass 的多个条件
- python - 如何使用布尔逻辑使这个 if 语句在 python 3 中更简洁?
- java - Java:在 Thread.start() 之前和之后更新变量
- spring - 无法从 start.spring.io 启动 Spring Cloud Config Server
- c++ - 在 C++ 中使用移动语义传递 unique_ptr 的向量
- r - 将 R 与 NodeJS 集成
- php - 如何在 laravel 中停止正在运行的排队作业?
- sorting - 如何在groovy中进行智能排序?
- sql - SQL- ORDER BY DESC 未正确获取结果