You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中Collectors.groupingBy优化:单步获取各年级最高分学生

获取各年级总分最高学生的单步Stream优化方案

问题背景

需要从学生成绩数据中,按年级(standard)分组后提取每组中总分(totalMarks)最高的学生。当前实现用了两步Stream操作,希望优化为单步完成。

示例数据

{
  "students": [
    {
      "fullName": "Student 1",
      "standard": 10,
      "rollNumber": "1R",
      "house": "Red",
      "marks": {
        "maths": 90,
        "english": 90,
        "science": 80,
        "history": 90
      },
      "totalMarks": 350,
      "sports": [
        {
          "sportName": "karate",
          "status": "purple-belt"
        },
        {
          "sportName": "swimming",
          "status": "swimmer"
        },
        {
          "sportName": "karate",
          "status": "green-belt"
        }
      ],
      "hobbies": [
        "programming",
        "reading"
      ]
    },
    {
      "fullName": "Student 2",
      "standard": 10,
      "rollNumber": "2R",
      "house": "Blue",
      "marks": {
        "maths": 90,
        "english": 90,
        "science": 90,
        "history": 90
      },
      "totalMarks": 360,
      "sports": [
        {
          "sportName": "cricket",
          "status": "all-rounder"
        }
      ],
      "hobbies": [
        "gaming"
      ]
    }
  ]
}

现有实现代码

Map<Integer, List<Student>> map8 = reportCard.getStudents().stream().collect(Collectors.groupingBy(Student::getStandard));
map8.keySet().forEach(key -> map8.put(key, map8.get(key).stream().reduce((st1,st2) -> st1.getTotalMarks() > st2.getTotalMarks()? st1:st2).stream().toList()));
map8.forEach((key, value) -> System.out.println(key + "-" + value));

优化后的单步实现

可以利用Collectors.groupingBy的重载方法,直接在分组时指定下游收集器完成最高分学生的提取,无需后续遍历修改Map。

方案1:返回Map<Integer, Student>(推荐,更简洁)

Map<Integer, Student> topStudentPerGrade = reportCard.getStudents().stream()
        .collect(Collectors.groupingBy(
                Student::getStandard,
                Collectors.collectingAndThen(
                        Collectors.maxBy(Comparator.comparingInt(Student::getTotalMarks)),
                        optStudent -> optStudent.orElse(null) // 空分组时返回null,可根据需求替换为默认学生对象
                )
        ));

// 输出结果
topStudentPerGrade.forEach((grade, student) -> 
        System.out.printf("年级%d: %s,总分%d%n", grade, student.getFullName(), student.getTotalMarks())
);

方案2:保留Map<Integer, List<Student>>结构

如果需要和原有代码的返回类型一致(每个年级对应一个含最高分学生的列表),可调整如下:

Map<Integer, List<Student>> topStudentListPerGrade = reportCard.getStudents().stream()
        .collect(Collectors.groupingBy(
                Student::getStandard,
                Collectors.collectingAndThen(
                        Collectors.maxBy(Comparator.comparingInt(Student::getTotalMarks)),
                        opt -> opt.map(Collections::singletonList).orElse(Collections.emptyList())
                )
        ));

topStudentListPerGrade.forEach((key, value) -> System.out.println(key + "-" + value));

关键逻辑说明

  • Collectors.groupingBy(Student::getStandard):按年级完成分组。
  • Collectors.maxBy(Comparator.comparingInt(Student::getTotalMarks)):在每个分组内部,通过比较totalMarks的数值,筛选出分数最高的学生,返回Optional<Student>(避免空分组时抛出异常)。
  • Collectors.collectingAndThen(...):将Optional<Student>转换为最终需要的类型——要么直接提取学生对象,要么包装成单元素列表,同时处理空分组的边界情况。

内容的提问来源于stack exchange,提问作者astar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 21:12:45