
本教程详细介绍了如何使用Java Stream API高效处理学生成绩数据。内容涵盖从数据收集、利用Collectors.toMap将学生多门成绩转换为平均分、到使用流操作进行过滤、以及最终通过Map.Entry.comparingByValue进行降序排序并格式化输出。通过优化计算逻辑和利用Stream API的强大功能,避免了重复计算,提升了代码的简洁性和执行效率。
1. 问题背景与数据处理需求
在实际应用中,我们经常需要处理结构化数据,例如学生成绩。本教程的目标是构建一个程序,能够:
读取指定数量的学生姓名及其对应的多门成绩。记录每位学生的全部成绩。计算每位学生的平均分。筛选出平均分高于或等于4.50的学生。将筛选后的学生按照平均分降序排列。以特定格式(”{姓名} -> {平均分}”,平均分保留两位小数)输出结果。
2. 初始数据收集与存储
首先,我们需要一个数据结构来存储学生的姓名和他们的多门成绩。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> 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)将成绩添加到对应的列表中。
3. Stream API优化:计算平均分、过滤与排序
原始的处理方式可能会在过滤和排序阶段多次计算学生的平均分,这不仅效率低下,而且在进行浮点数比较时,直接将double类型转换为int进行排序(如(int) (average2 – average1))可能会导致精度丢失,从而产生错误的排序结果。
为了解决这些问题,我们可以采用以下优化策略:
立即学习“Java免费学习笔记(深入)”;
一次性计算平均分并存储: 将Map<String, List>转换为Map,其中键是学生姓名,值是其计算好的平均分。利用Map.Entry的比较器: Java 8的Stream API提供了强大的Comparator辅助方法,特别是针对Map.Entry的排序,可以避免手动编写复杂的比较逻辑。
3.1 转换数据结构:计算平均分
我们可以使用Collectors.toMap将原始的studentRecords映射转换为一个新的Map,其中存储的是学生的姓名及其平均分。
序列猴子开放平台
具有长序列、多模态、单模型、大数据等特点的超大规模语言模型
0 查看详情
Map recordsWithAverage = studentRecords.entrySet() .stream() // 将每个Map.Entry<String, List>转换为Map.Entry // 键保持不变,值为对应List的平均值 .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转换为DoubleStream,以便进行平均值计算。orElse(0.0)用于处理学生没有成绩的情况(尽管在此问题中不会发生,但作为健壮性考虑是好的实践)。
3.2 过滤与排序
现在我们有了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逻辑,代码更加简洁易懂。
4. 完整代码示例
将上述所有部分整合起来,完整的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> 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 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 }}
5. 注意事项与总结
避免重复计算: 将平均分计算为独立的一步,避免在过滤和排序过程中反复计算,显著提高了效率。使用Double.compare()或内置比较器: 对于浮点数比较,应使用Double.compare()或像Map.Entry.comparingByValue()这样的内置比较器,而不是将差值强制转换为int,以避免精度问题和不正确的排序结果。Stream API的链式操作: Stream API允许将多个操作(如filter、sorted、forEach)链式调用,使代码更具可读性和表达力。Collectors.toMap的强大功能: Collectors类提供了多种强大的收集器,能够将流中的元素转换成不同的数据结构,是Stream API中不可或缺的一部分。资源管理: 养成关闭Scanner等资源的好习惯,避免资源泄露。
通过本教程,您应该已经掌握了如何使用Java Stream API高效地处理学生成绩数据,包括数据的收集、转换、过滤和排序,并了解了如何优化代码以提高性能和准确性。
以上就是Java Stream API:高效处理学生成绩数据并按平均分排序教程的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/344132.html
微信扫一扫
支付宝扫一扫