在实际应用中,我们经常需要处理结构化数据,例如学生成绩。本教程的目标是构建一个程序,能够:
首先,我们需要一个数据结构来存储学生的姓名和他们的多门成绩。HashMap是一个理想的选择,其中键为学生姓名(String),值为一个包含该学生所有成绩的列表(List
import java.util.*; import java.util.stream.Collectors; public class StudentGradesProcessor { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = Integer.parseInt(scanner.nextLine()); // 读取学生数量 Map<String, List<Double>> studentRecords = new HashMap<>(); // 循环读取学生姓名和成绩 while (n > 0) { String name = scanner.nextLine(); double grade = Double.parseDouble(scanner.nextLine()); // 如果学生不存在,则添加新条目;否则,将成绩添加到现有列表中 studentRecords.putIfAbsent(name, new ArrayList<>()); studentRecords.get(name).add(grade); n--; } // 后续处理将在下方详细介绍 } }
在上述代码中,我们使用putIfAbsent方法确保每个学生姓名只创建一次列表,然后通过get(name).add(grade)将成绩添加到对应的列表中。
原始的处理方式可能会在过滤和排序阶段多次计算学生的平均分,这不仅效率低下,而且在进行浮点数比较时,直接将double类型转换为int进行排序(如(int) (average2 - average1))可能会导致精度丢失,从而产生错误的排序结果。
为了解决这些问题,我们可以采用以下优化策略:
立即学习“Java免费学习笔记(深入)”;
我们可以使用Collectors.toMap将原始的studentRecords映射转换为一个新的Map,其中存储的是学生的姓名及其平均分。
Map<String, Double> recordsWithAverage = studentRecords.entrySet() .stream() // 将每个Map.Entry<String, List<Double>>转换为Map.Entry<String, Double> // 键保持不变,值为对应List<Double>的平均值 .collect(Collectors.toMap( Map.Entry::getKey, // 新Map的键是原始Map的键(学生姓名) e -> e.getValue().stream().mapToDouble(Double::doubleValue).average().orElse(0.0) // 新Map的值是平均分 ));
这里,e -> e.getValue().stream().mapToDouble(Double::doubleValue).average().orElse(0.0)负责计算每个学生所有成绩的平均值。mapToDouble(Double::doubleValue)将Stream
现在我们有了recordsWithAverage,其中每个学生都对应一个已经计算好的平均分。接下来的过滤和排序操作将变得非常简洁和高效。
recordsWithAverage.entrySet() .stream() // 过滤:只保留平均分大于或等于4.50的学生 .filter(e -> e.getValue() >= 4.50) // 排序:按平均分降序排列 // Map.Entry.comparingByValue() 返回一个Comparator,用于比较Map.Entry的值 // Comparator.reverseOrder() 将比较器反转,实现降序排列 .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) // 遍历并打印结果 .forEach(pair -> { System.out.printf("%s -> %.2f%n", pair.getKey(), pair.getValue()); });
这里的关键是Map.Entry.comparingByValue(Comparator.reverseOrder())。它直接作用于Map.Entry对象,根据其值进行比较,并且Comparator.reverseOrder()确保了降序排列。这样,我们无需手动编写复杂的Comparator逻辑,代码更加简洁易懂。
将上述所有部分整合起来,完整的Java程序如下:
import java.util.*; import java.util.stream.Collectors; public class StudentGradesProcessor { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = Integer.parseInt(scanner.nextLine()); // 步骤1: 收集原始学生姓名和成绩数据 Map<String, List<Double>> studentRecords = new HashMap<>(); while (n > 0) { String name = scanner.nextLine(); double grade = Double.parseDouble(scanner.nextLine()); studentRecords.putIfAbsent(name, new ArrayList<>()); studentRecords.get(name).add(grade); n--; } // 步骤2: 计算每个学生的平均分,并存储到新的Map中 Map<String, Double> recordsWithAverage = studentRecords.entrySet() .stream() .collect(Collectors.toMap( Map.Entry::getKey, e -> e.getValue().stream().mapToDouble(Double::doubleValue).average().orElse(0.0) )); // 步骤3: 过滤、排序并打印结果 recordsWithAverage.entrySet() .stream() .filter(e -> e.getValue() >= 4.50) // 过滤:平均分大于等于4.50 .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) // 排序:按平均分降序 .forEach(pair -> { System.out.printf("%s -> %.2f%n", pair.getKey(), pair.getValue()); // 格式化输出 }); scanner.close(); // 关闭Scanner } }
通过本教程,您应该已经掌握了如何使用Java Stream API高效地处理学生成绩数据,包括数据的收集、转换、过滤和排序,并了解了如何优化代码以提高性能和准确性。
以上就是Java Stream API:高效处理学生成绩数据并按平均分排序教程的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号