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

使用Stream API计算含0分任务的平均分问题排查

问题排查:包含未完成0分任务的平均分计算错误

问题背景

我需要实现两个方法来计算包含学生未尝试完成的0分任务在内的平均分,但遇到0分场景时计算结果不符合预期。具体需求:

  • totalScores:返回学生到其总平均分的映射;
  • averageTotalScore:返回所有学生的平均结果,需考虑课程的全部任务而非仅学生完成的任务。

举例说明:若一名学生有4个实验任务,其中3个完成并获得分数,1个未尝试(应按0分计算),但当前totalScores方法计算平均分时未计入该0分任务,导致实际结果与预期不符(如Eva Bee的实际结果为77.00,预期为57.75)。

现有代码

CourseResult类

public class CourseResult {
    private final Person person;
    private final Map<String, Integer> taskResults;
    public CourseResult(final Person person, final Map<String, Integer> taskResults) {
        this.person = person;
        this.taskResults = taskResults;
    }
    public Person getPerson() {
        return person;
    }
    public Map<String, Integer> getTaskResults() {
        return taskResults;
    }
}

有问题的totalScores方法

public static Map<Person, Double> totalScores(Stream<CourseResult> results) {
    Map<Person, List<Integer>> scores = results
            .flatMap(cr -> cr.getTaskResults().entrySet().stream()
                    .filter(e -> e.getValue() >= 0)
                    .map(e -> new AbstractMap.SimpleEntry<>(cr.getPerson(), e.getValue())))
            .collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.mapping(e -> Optional.ofNullable(e.getValue()).orElse(0), Collectors.toList())));

    Map<Person, Double> averageScores = new HashMap<>();
    scores.forEach((person, taskResults) -> {
        int totalScore = taskResults.stream()
                .mapToInt(Integer::intValue)
                .sum();
        double tasksPerPerson = scores.get(person).stream()
                .mapToInt(Integer::intValue)
                .filter(score -> score >= 0)
                .count();

        double averageScore = totalScore / tasksPerPerson;
        averageScores.put(person, averageScore);
    });
    return averageScores;
}

问题根源

  1. 丢失未尝试任务:当前逻辑仅处理学生已提交的任务(taskResults中存在的条目),完全没考虑课程中存在但学生未尝试的任务——核心问题是你没有传入课程总任务列表,无法知道学生有多少未完成的任务需要按0分计算。
  2. 不必要的过滤:
    • flatMap阶段的.filter(e -> e.getValue() >= 0)会过滤掉值为负数的分数,若未尝试任务用null表示,这里会直接抛出NPE;
    • 计算任务数量时的.filter(score -> score >= 0)会把0分任务排除,导致分母偏小,平均分偏高。
  3. 冗余的Optional处理:Optional.ofNullable(e.getValue()).orElse(0)在已经过滤e.getValue() >=0的情况下毫无意义,非null的正数不会触发orElse分支。

修复方案

要解决问题,必须先获取课程的所有任务列表,才能统计每个学生的总任务数(已完成+未完成)。以下是修复后的代码:

修复后的完整实现

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CourseScoreCalculator {

    // 修改方法签名,传入课程的所有任务集合,确保能统计未完成任务
    public static Map<Person, Double> totalScores(Stream<CourseResult> results, Set<String> allCourseTasks) {
        return results.collect(Collectors.toMap(
                CourseResult::getPerson,
                courseResult -> calculateStudentAverage(courseResult, allCourseTasks)
        ));
    }

    // 单独提取学生平均分计算逻辑,提高可读性
    private static double calculateStudentAverage(CourseResult courseResult, Set<String> allCourseTasks) {
        Map<String, Integer> studentTaskScores = courseResult.getTaskResults();
        int totalScore = 0;
        int totalTaskCount = allCourseTasks.size();

        // 遍历所有课程任务,未完成的自动按0分累加
        for (String taskName : allCourseTasks) {
            totalScore += studentTaskScores.getOrDefault(taskName, 0);
        }

        // 避免除以0的情况(如果课程没有任务)
        return totalTaskCount == 0 ? 0.0 : (double) totalScore / totalTaskCount;
    }

    // 实现averageTotalScore方法:计算所有学生平均分的平均值
    public static double averageTotalScore(Stream<CourseResult> results, Set<String> allCourseTasks) {
        Map<Person, Double> studentAverages = totalScores(results, allCourseTasks);
        return studentAverages.values().stream()
                .mapToDouble(Double::doubleValue)
                .average()
                // 如果没有学生,返回0.0
                .orElse(0.0);
    }
}

修复说明

  • 新增课程任务参数:通过传入allCourseTasks集合,明确课程的总任务数,确保未尝试的任务被计入;
  • 按任务列表计算分数:遍历所有课程任务,用getOrDefault自动将未完成任务的分数设为0;
  • 移除冗余过滤:不再过滤任何分数,确保0分任务被正确统计;
  • 异常防护:处理了课程无任务、无学生的边界情况;
  • 逻辑拆分:将学生平均分计算拆分为私有方法,代码更清晰易维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 13:47:51