使用Java Stream API按科目筛选成绩前三的学生
Java Stream API按科目筛选成绩前三的学生
嘿,看起来你已经走对方向了!分组科目这一步已经完成,接下来只需要给每个分组加上按成绩降序排序和截取前3名的逻辑就行。我来给你一步步拆解怎么实现:
核心思路
- 用
flatMap把每个学生的科目列表展开成单个的Subject流(你已经做到啦) - 按科目名称分组,同时对每个分组内的
Subject对象按分数从高到低排序 - 对排序后的分组列表,只保留前3个元素,就是该科目成绩前三的学生
完整实现代码
把你原来的studentMap代码替换成下面这段,并且可以加上结果打印的逻辑:
// 按科目分组,每组内按成绩降序排序后取前3 Map<String, List<Subject>> topStudentsBySubject = studentList.stream() .flatMap(student -> student.getSubjectList().stream()) .collect(Collectors.groupingBy( Subject::getSubjectName, Collectors.collectingAndThen( Collectors.toList(), list -> list.stream() .sorted(Comparator.comparingDouble(Subject::getMarks).reversed()) .limit(3) .collect(Collectors.toList()) ) )); // 打印结果,清晰展示每个科目前三的学生 topStudentsBySubject.forEach((subjectName, topStudents) -> { System.out.println("=== " + subjectName + " 成绩前三的学生 ==="); topStudents.forEach(subject -> { // 根据学生ID找到对应的学生信息 Student student = studentList.stream() .filter(s -> s.getStudentId().equals(subject.getStudentId())) .findFirst() .orElse(null); if (student != null) { System.out.printf("学生ID: %d, 姓名: %s, %s成绩: %.1f%n", student.getStudentId(), student.getName(), subjectName, subject.getMarks()); } }); System.out.println(); });
代码解释
Collectors.groupingBy(Subject::getSubjectName, ...):第一个参数是分组的键(科目名称),第二个参数是下游收集器,用来处理每个分组的元素Collectors.collectingAndThen(...):这个收集器可以先把元素收集成列表,然后对列表做进一步处理——这里就是先排序,再取前3sorted(Comparator.comparingDouble(Subject::getMarks).reversed()):按分数降序排序,reversed()是把默认的升序改成降序limit(3):只保留排序后的前3个元素,也就是成绩最高的三个
小优化建议
你现在的Subject类里存了studentId,要关联学生信息还得再查一遍列表。如果想更高效,可以在Subject里直接持有Student对象的引用,这样打印的时候就不用额外查询啦,代码会更简洁。
比如修改Subject类:
class Subject{ private Student student; // 直接关联Student对象 private String subjectName; private Double marks; // 构造方法、getter/setter、toString都对应修改 public Subject(Student student, String subjectName, Double marks) { this.student = student; this.subjectName = subjectName; this.marks = marks; } }
这样创建Subject的时候直接传入Student对象,后续打印就可以直接从Subject里获取学生信息啦。
备注:内容来源于stack exchange,提问作者Sanjay Naik
相关产品推荐
相关产品推荐

