使用Java 8 Streams API按两个字段分组并返回更新后的集合
解决Java 8 Stream按部门+薪资分组并筛选多记录分组的问题
要实现按dept和salary联合分组,并只保留分组内记录数大于1的所有员工,核心是先构造复合分组键,再通过Stream完成分组、过滤、扁平化处理。
1. 确认Employee类实现
假设你的Employee类已实现如下(包含字段、构造器、Getter/Setter):
public class Employee { private String name; private String dept; private int age; private double salary; public Employee(String name, String dept, int age, double salary) { this.name = name; this.dept = dept; this.age = age; this.salary = salary; } // Getter和Setter方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDept() { return dept; } public void setDept(String dept) { this.dept = dept; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } // 重写toString方便调试 @Override public String toString() { return "Employee{" + "name='" + name + '\'' + ", dept='" + dept + '\'' + ", age=" + age + ", salary=" + salary + '}'; } }
2. Stream核心实现逻辑
用AbstractMap.SimpleEntry作为复合分组键(兼容Java 8),完成分组、过滤、扁平化流程:
import java.util.AbstractMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class EmployeeFilter { public static List<Employee> getEmployeesFromLargeGroups(List<Employee> employeeList) { // 1. 按dept+salary联合分组 Map<Map.Entry<String, Double>, List<Employee>> groupedByDeptAndSalary = employeeList.stream() .collect(Collectors.groupingBy(emp -> new AbstractMap.SimpleEntry<>(emp.getDept(), emp.getSalary()))); // 2. 筛选分组大小>1的集合,再扁平化为单个员工列表 return groupedByDeptAndSalary.values().stream() .filter(group -> group.size() > 1) .flatMap(List::stream) .collect(Collectors.toList()); } // 测试示例 public static void main(String[] args) { List<Employee> employees = List.of( new Employee("Alice", "HR", 25, 5000.0), new Employee("Bob", "HR", 30, 5000.0), // 和Alice同部门同薪资,保留 new Employee("Charlie", "Engineering", 35, 8000.0), new Employee("David", "Engineering", 28, 7500.0), // 单独分组,排除 new Employee("Eve", "Engineering", 32, 8000.0), // 和Charlie同部门同薪资,保留 new Employee("Frank", "Finance", 40, 9000.0) // 单独分组,排除 ); List<Employee> result = getEmployeesFromLargeGroups(employees); result.forEach(System.out::println); } }
3. 代码说明
- 复合分组键:
AbstractMap.SimpleEntry已实现equals()和hashCode(),可直接作为Map的key,将dept和salary绑定为唯一分组标识。 - 分组过滤:分组后仅保留元素数量大于1的分组,排除单条记录的分组。
- 扁平化处理:通过
flatMap()将多个符合条件的分组列表合并为一个员工列表。
4. Java 16+优化方案
如果使用Java 16及以上,可改用**记录类(Record)**替代SimpleEntry,代码可读性更强:
// 定义记录类作为分组键 public record DeptSalaryGroup(String dept, double salary) {} // 修改分组逻辑 Map<DeptSalaryGroup, List<Employee>> grouped = employeeList.stream() .collect(Collectors.groupingBy(emp -> new DeptSalaryGroup(emp.getDept(), emp.getSalary())));
运行测试代码后,输出会保留Alice、Bob、Charlie、Eve,排除David和Frank,完全符合需求。
内容的提问来源于stack exchange,提问作者Anil Nivargi
相关产品推荐
相关产品推荐

