如何在Java中按多字段对List<Student>进行分组?
使用Java Stream实现按多属性分组
当然可以通过Java Stream结合Collectors.groupingBy来高效实现这个分组需求,无需手动遍历排序,代码更简洁且可读性更强。以下是几种可行的方案:
方案1:Java 16+ 用Record作为分组键(推荐)
Java 16引入的Record是不可变数据载体,自动实现了equals()和hashCode(),非常适合作为分组的键:
首先定义一个Record来封装分组的属性:
// 定义分组键,包含age和avgGrade两个属性 record AgeGradeKey(int age, int avgGrade) {}
然后通过Stream完成分组:
List<List<Student>> stds = students.stream() // 按AgeGradeKey分组,将相同age和avgGrade的Student归为一组 .collect(Collectors.groupingBy(student -> new AgeGradeKey(student.getAge(), student.getAvgGrade()))) // 提取分组后的所有值(即每个子列表),转为List<List<Student>> .values() .stream() .toList(); // Java 16+ 可用toList(),更早版本替换为Collectors.toList()
方案2:用List作为分组键(无需额外类)
如果不想定义新的类,可以直接用List.of()创建包含age和avgGrade的列表作为分组键,因为List的equals()和hashCode()是基于元素内容实现的:
List<List<Student>> stds = students.stream() .collect(Collectors.groupingBy(student -> List.of(student.getAge(), student.getAvgGrade()))) .values() .stream() .collect(Collectors.toList());
方案3:Java 16之前 自定义分组键类
如果使用Java 16之前的版本,需要自定义一个类并重写equals()和hashCode()来保证分组逻辑正确:
import java.util.Objects; class AgeGradeKey { private final int age; private final int avgGrade; public AgeGradeKey(int age, int avgGrade) { this.age = age; this.avgGrade = avgGrade; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AgeGradeKey that = (AgeGradeKey) o; return age == that.age && avgGrade == that.avgGrade; } @Override public int hashCode() { return Objects.hash(age, avgGrade); } }
然后同样通过Stream分组:
List<List<Student>> stds = students.stream() .collect(Collectors.groupingBy(student -> new AgeGradeKey(student.getAge(), student.getAvgGrade()))) .values() .stream() .collect(Collectors.toList());
这些方案的优势在于:利用Stream API的内置优化,代码逻辑清晰,避免了手动遍历、排序和分组的冗余代码,同时降低了出错概率。
内容的提问来源于stack exchange,提问作者PuneethC99
相关产品推荐
相关产品推荐

