Stream API实现分组学生GPA计算报错:无法填充Map求助
问题:Stream处理分组学生GPA计算出错
我搞不懂自己的代码为啥跑不起来,Stream部分肯定有问题,没法把List的数据填充到Map里。我要计算每个学生的总GPA,最终结果是一个嵌套Map——外层键是分组名,内层键是学生姓名,值是该学生的总GPA。
原代码
class Student4 { String name; Subject4[] subjects; public Student4(String name, Subject4[] subjects) { this.name = name; this.subjects = subjects; } public static void main(String[] args) { Student4 s1 = new Student4("Name1", new Subject4[]{ new Subject4("A", new Integer[]{4, 4, 4, 4, 5}), //21 -> 4.2 new Subject4("B", new Integer[]{5, 5, 5, 5, 5}), //25 -> 5.0 new Subject4("C", new Integer[]{4, 4, 4, 4, 5}) //21 -> 4.2 }); Student4 s2 = new Student4("Name2", new Subject4[]{ new Subject4("A", new Integer[]{3, 3, 3, 4, 5}), //18 -> 3.6 new Subject4("B", new Integer[]{5, 3, 3, 5, 5}), //21 -> 4.2 new Subject4("C", new Integer[]{4, 3, 3, 4, 3}) //17 -> 3.4 }); Student4 s3 = new Student4("Name3", new Subject4[]{ new Subject4("A", new Integer[]{5, 5, 5, 4, 5}), //24 -> 4.8 new Subject4("B", new Integer[]{5, 5, 5, 4, 5}), //24 -> 4.8 new Subject4("C", new Integer[]{5, 5, 5, 4, 5}) //24 -> 4.8 }); Student4 s4 = new Student4("Name4", new Subject4[]{ new Subject4("A", new Integer[]{5, 5, 5, 4, 5}), //24 -> 4.8 new Subject4("B", new Integer[]{5, 5, 5, 4, 5}), //24 -> 4.8 new Subject4("C", new Integer[]{5, 5, 5, 4, 5}) //24 -> 4.8 }); Map<String, List<Student4>> group = new HashMap<>(); group.put("group1", List.of(s1, s2)); group.put("group2", List.of(s3, s4)); Map<String, Map<String, Double>> averageByStudent = group.entrySet().stream() .collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue().stream() .collect(Collectors.toMap(entry.getValue().stream() .map(z -> z.name), entry.getValue().stream() .flatMap(v -> Arrays.stream(v.subjects)) .flatMap(w -> Arrays.stream(w.mark)) .mapToDouble(Integer::doubleValue) .average().getAsDouble() )))); } } class Subject4 { String name; Integer[] mark; public Subject4(String name, Integer[] mark) { this.name = name; this.mark = mark; } }
错误分析
- 内层
Collectors.toMap参数错误:你把整个分组的学生Stream直接传给keyMapper,不符合API要求——keyMapper应该是针对每个Student4对象的函数,返回该学生的姓名作为键。 - GPA计算逻辑错误:原代码把整个分组的所有学生成绩混在一起求平均,这不是单个学生的总GPA,应该针对每个学生单独计算其所有科目成绩的平均值。
修正后的代码
import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; class Student4 { String name; Subject4[] subjects; public Student4(String name, Subject4[] subjects) { this.name = name; this.subjects = subjects; } // 计算当前学生的总GPA public double calculateTotalGPA() { return Arrays.stream(subjects) .flatMap(subject -> Arrays.stream(subject.mark)) .mapToDouble(Integer::doubleValue) .average() .orElse(0.0); // 无成绩时返回0.0,避免空指针 } public static void main(String[] args) { Student4 s1 = new Student4("Name1", new Subject4[]{ new Subject4("A", new Integer[]{4, 4, 4, 4, 5}), //21 -> 4.2 new Subject4("B", new Integer[]{5, 5, 5, 5, 5}), //25 -> 5.0 new Subject4("C", new Integer[]{4, 4, 4, 4, 5}) //21 -> 4.2 }); Student4 s2 = new Student4("Name2", new Subject4[]{ new Subject4("A", new Integer[]{3, 3, 3, 4, 5}), //18 -> 3.6 new Subject4("B", new Integer[]{5, 3, 3, 5, 5}), //21 -> 4.2 new Subject4("C", new Integer[]{4, 3, 3, 4, 3}) //17 -> 3.4 }); Student4 s3 = new Student4("Name3", new Subject4[]{ new Subject4("A", new Integer[]{5, 5, 5, 4, 5}), //24 -> 4.8 new Subject4("B", new Integer[]{5, 5, 5, 4, 5}), //24 -> 4.8 new Subject4("C", new Integer[]{5, 5, 5, 4, 5}) //24 -> 4.8 }); Student4 s4 = new Student4("Name4", new Subject4[]{ new Subject4("A", new Integer[]{5, 5, 5, 4, 5}), //24 -> 4.8 new Subject4("B", new Integer[]{5, 5, 5, 4, 5}), //24 -> 4.8 new Subject4("C", new Integer[]{5, 5, 5, 4, 5}) //24 -> 4.8 }); Map<String, List<Student4>> group = new HashMap<>(); group.put("group1", List.of(s1, s2)); group.put("group2", List.of(s3, s4)); // 修正后的Stream处理逻辑 Map<String, Map<String, Double>> averageByStudent = group.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, // 外层Map的键:分组名 entry -> entry.getValue().stream() .collect(Collectors.toMap( Student4::getName, // 内层Map的键:学生姓名 Student4::calculateTotalGPA // 内层Map的值:学生总GPA )) )); // 打印结果验证 averageByStudent.forEach((groupName, studentGPAs) -> { System.out.println("分组:" + groupName); studentGPAs.forEach((name, gpa) -> System.out.println(name + " - " + gpa)); }); } // 封装字段访问 public String getName() { return name; } } class Subject4 { String name; Integer[] mark; public Subject4(String name, Integer[] mark) { this.name = name; this.mark = mark; } }
修正说明
- 新增
calculateTotalGPA()方法:把单个学生的GPA计算逻辑封装起来,代码更清晰易维护。 - 修正Stream嵌套逻辑:外层处理分组,内层针对每个学生,用
Student4::getName作为内层Map的键,Student4::calculateTotalGPA作为对应的值,完全匹配需求。 - 新增
getName()方法:遵循封装原则,避免直接访问成员变量。 - 用
orElse(0.0)处理空值:如果学生没有任何成绩,避免抛出NoSuchElementException。
内容的提问来源于stack exchange,提问作者R Zav
相关产品推荐
相关产品推荐

