0

0

Java Stream API:高效处理学生成绩数据并按平均分排序教程

聖光之護

聖光之護

发布时间:2025-07-16 11:18:49

|

194人浏览过

|

来源于php中文网

原创

Java Stream API:高效处理学生成绩数据并按平均分排序教程

本教程详细介绍了如何使用Java Stream API高效处理学生成绩数据。内容涵盖从数据收集、利用Collectors.toMap将学生多门成绩转换为平均分、到使用流操作进行过滤、以及最终通过Map.Entry.comparingByValue进行降序排序并格式化输出。通过优化计算逻辑和利用Stream API的强大功能,避免了重复计算,提升了代码的简洁性和执行效率。

1. 问题背景与数据处理需求

在实际应用中,我们经常需要处理结构化数据,例如学生成绩。本教程的目标是构建一个程序,能够:

  1. 读取指定数量的学生姓名及其对应的多门成绩。
  2. 记录每位学生的全部成绩。
  3. 计算每位学生的平均分。
  4. 筛选出平均分高于或等于4.50的学生。
  5. 将筛选后的学生按照平均分降序排列
  6. 以特定格式("{姓名} -> {平均分}",平均分保留两位小数)输出结果。

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> 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免费学习笔记(深入)”;

  1. 一次性计算平均分并存储: 将Map>转换为Map,其中键是学生姓名,值是其计算好的平均分。
  2. 利用Map.Entry的比较器: Java 8的Stream API提供了强大的Comparator辅助方法,特别是针对Map.Entry的排序,可以避免手动编写复杂的比较逻辑。

3.1 转换数据结构:计算平均分

我们可以使用Collectors.toMap将原始的studentRecords映射转换为一个新的Map,其中存储的是学生的姓名及其平均分。

Skybox AI
Skybox AI

一键将涂鸦转为360°无缝环境贴图的AI神器

下载
        Map recordsWithAverage = studentRecords.entrySet()
            .stream()
            // 将每个Map.Entry>转换为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> 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高效地处理学生成绩数据,包括数据的收集、转换、过滤和排序,并了解了如何优化代码以提高性能和准确性。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

463

2023.08.02

php中foreach用法
php中foreach用法

本专题整合了php中foreach用法的相关介绍,阅读专题下面的文章了解更多详细教程。

76

2025.12.04

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

463

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

544

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

113

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

200

2025.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

113

2025.08.29

C++中int、float和double的区别
C++中int、float和double的区别

本专题整合了c++中int和double的区别,阅读专题下面的文章了解更多详细内容。

102

2025.10.23

C++ 设计模式与软件架构
C++ 设计模式与软件架构

本专题深入讲解 C++ 中的常见设计模式与架构优化,包括单例模式、工厂模式、观察者模式、策略模式、命令模式等,结合实际案例展示如何在 C++ 项目中应用这些模式提升代码可维护性与扩展性。通过案例分析,帮助开发者掌握 如何运用设计模式构建高质量的软件架构,提升系统的灵活性与可扩展性。

9

2026.01.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号