本文目录导读:

在Java中按字段分组数据,最常用的方法是使用 Stream API 的 Collectors.groupingBy() 方法(Java 8+),以下是几种常见场景的示例:
基础示例:按单个字段分组
import java.util.*;
import java.util.stream.Collectors;
public class GroupByExample {
public static void main(String[] args) {
// 创建数据
List<Student> students = Arrays.asList(
new Student("张三", "A班", 85),
new Student("李四", "B班", 92),
new Student("王五", "A班", 78),
new Student("赵六", "B班", 95),
new Student("钱七", "A班", 88)
);
// 按班级分组
Map<String, List<Student>> groupByClass = students.stream()
.collect(Collectors.groupingBy(Student::getClassName));
// 打印结果
groupByClass.forEach((className, studentList) -> {
System.out.println("班级: " + className);
studentList.forEach(s -> System.out.println(" " + s.getName()));
});
}
}
class Student {
private String name;
private String className;
private int score;
// 构造方法、getter/setter省略
public Student(String name, String className, int score) {
this.name = name;
this.className = className;
this.score = score;
}
public String getName() { return name; }
public String getClassName() { return className; }
public int getScore() { return score; }
}
按多个字段分组
// 创建复合键类
class ClassGradeKey {
private String className;
private String grade;
public ClassGradeKey(String className, String grade) {
this.className = className;
this.grade = grade;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ClassGradeKey that = (ClassGradeKey) o;
return Objects.equals(className, that.className) &&
Objects.equals(grade, that.grade);
}
@Override
public int hashCode() {
return Objects.hash(className, grade);
}
}
// 使用示例
Map<ClassGradeKey, List<Student>> multiGroup = students.stream()
.collect(Collectors.groupingBy(
s -> new ClassGradeKey(s.getClassName(), s.getGrade())
));
分组后统计数据
// 按班级分组,统计每个班的人数
Map<String, Long> countByClass = students.stream()
.collect(Collectors.groupingBy(
Student::getClassName,
Collectors.counting()
));
// 按班级分组,计算平均分
Map<String, Double> avgScoreByClass = students.stream()
.collect(Collectors.groupingBy(
Student::getClassName,
Collectors.averagingInt(Student::getScore)
));
// 按班级分组,找出最高分
Map<String, Optional<Student>> maxScoreByClass = students.stream()
.collect(Collectors.groupingBy(
Student::getClassName,
Collectors.maxBy(Comparator.comparingInt(Student::getScore))
));
分组后转换数据
// 按班级分组,只提取姓名列表
Map<String, List<String>> namesByClass = students.stream()
.collect(Collectors.groupingBy(
Student::getClassName,
Collectors.mapping(Student::getName, Collectors.toList())
));
// 按班级分组,将学生对象转换为分数列表
Map<String, List<Integer>> scoresByClass = students.stream()
.collect(Collectors.groupingBy(
Student::getClassName,
Collectors.mapping(Student::getScore, Collectors.toList())
));
使用传统方式(Java 8之前)
// 传统方式分组
Map<String, List<Student>> groupByClass = new HashMap<>();
for (Student student : students) {
String className = student.getClassName();
groupByClass.computeIfAbsent(className, k -> new ArrayList<>()).add(student);
}
处理复杂的分组场景
// 根据分数范围分组
Map<String, List<Student>> groupByScoreRange = students.stream()
.collect(Collectors.groupingBy(s -> {
int score = s.getScore();
if (score >= 90) return "优秀";
else if (score >= 80) return "良好";
else if (score >= 60) return "及格";
else return "不及格";
}));
// 按多个字段分组(使用字符串拼接作为key)
Map<String, List<Student>> groupByMultipleFields = students.stream()
.collect(Collectors.groupingBy(
s -> s.getClassName() + "-" + s.getGrade()
));
注意事项
-
排序问题:
groupingBy()不保证分组结果的顺序,如果需要有序可以使用groupingBy()的重载版本:// 按班级分组,保持插入顺序 Map<String, List<Student>> groupByClass = students.stream() .collect(Collectors.groupingBy( Student::getClassName, LinkedHashMap::new, // 使用LinkedHashMap保持顺序 Collectors.toList() )); -
null值处理:分组字段为null时,会创建key为null的分组,建议使用
Objects::nonNull过滤。
这些方法适用于大多数按字段分组的需求,选择哪种方式取决于你的具体业务场景。