0

0

Java Stream API:高效处理与排序学生平均成绩

碧海醫心

碧海醫心

发布时间:2025-07-16 13:18:16

|

1065人浏览过

|

来源于php中文网

原创

java stream api:高效处理与排序学生平均成绩

本文详细介绍了如何利用Java Stream API高效处理学生成绩数据。内容涵盖从原始输入中收集学生姓名及成绩,到计算每个学生的平均分,再到根据平均分进行筛选和降序排序,最终以指定格式输出结果。重点阐述了通过预计算平均分来优化性能,避免重复计算,并利用Stream API的强大功能实现简洁高效的数据处理流程。

1. 概述与问题背景

在数据处理场景中,我们经常需要对集合数据进行转换、筛选和排序。以学生成绩管理为例,常见的需求包括:收集学生的各科成绩,计算平均分,筛选出符合特定条件的(如平均分高于某个阈值)学生,并按平均分高低进行排序。Java 8引入的Stream API为这类数据处理提供了强大而简洁的解决方案。

本教程将演示如何使用Java Stream API来解决以下问题:

  1. 从输入中读取学生姓名和成绩。
  2. 存储每个学生的多个成绩。
  3. 计算每个学生的平均分。
  4. 筛选出平均分高于或等于特定阈值(例如4.50)的学生。
  5. 将筛选后的学生按平均分降序排列
  6. 以特定格式输出学生姓名及其平均分(保留两位小数)。

2. 数据收集与初始存储

首先,我们需要从标准输入读取数据。学生姓名和成绩是交替输入的,并且每个学生可能有多个成绩。为了存储这些数据,一个 Map> 是一个非常合适的选择,其中键是学生姓名,值是该学生所有成绩的列表。

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

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int n = Integer.parseInt(scanner.nextLine()); // 读取学生/成绩对的数量

        // 使用Map存储学生姓名及其对应的成绩列表
        Map> studentRecords = new HashMap<>();

        // 循环读取n对学生姓名和成绩
        while (n > 0) {
            String name = scanner.nextLine();
            double grade = Double.parseDouble(scanner.nextLine());

            // 如果学生不存在,则创建新的成绩列表;否则,将成绩添加到现有列表中
            studentRecords.putIfAbsent(name, new ArrayList<>());
            studentRecords.get(name).add(grade);
            n--;
        }
        // 关闭Scanner,释放资源
        scanner.close();

        // 后续处理将基于这个 studentRecords Map
    }
}

在上述代码中,putIfAbsent 方法确保如果学生姓名首次出现,会为他创建一个新的 ArrayList 来存储成绩。

立即学习Java免费学习笔记(深入)”;

3. Stream API处理:优化平均分计算与排序

原始问题中,用户在 filter 和 sorted 操作中都重复计算了学生的平均分,这会导致性能下降。更优的方法是先计算出每个学生的平均分,将其存储在一个新的 Map 中,然后再对这个新的Map进行筛选和排序。

Skybox AI
Skybox AI

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

下载

3.1 预计算平均分

我们可以使用Stream API的 collect(Collectors.toMap(...)) 操作将 Map> 转换为 Map,其中值是计算好的平均分。

        // 将原始学生成绩Map转换为学生姓名和其平均分的Map
        Map studentAverages = studentRecords.entrySet()
                .stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey, // 使用原始Map的键(学生姓名)作为新Map的键
                        entry -> entry.getValue().stream() // 获取成绩列表
                                .mapToDouble(Double::doubleValue) // 转换为DoubleStream
                                .average() // 计算平均值
                                .orElse(0.0) // 如果没有成绩(理论上不会发生),默认平均分为0.0
                ));

这一步是性能优化的关键。通过一次性计算并存储平均分,避免了在后续的过滤和排序操作中重复执行耗时的平均分计算。

3.2 筛选与排序

有了 studentAverages 这个 Map,接下来的筛选和排序就变得非常直接和高效。

  1. 筛选 (Filter): 使用 filter 操作过滤出平均分大于或等于4.50的学生。
  2. 排序 (Sort): 使用 sorted 操作,并结合 Map.Entry.comparingByValue(Comparator.reverseOrder()) 来按平均分(即Map的值)进行降序排序。Comparator.reverseOrder() 确保了降序排列。
  3. 输出 (ForEach): 最后使用 forEach 遍历结果,并使用 String.format 或 System.out.printf 来格式化输出,将平均分保留两位小数。
        // 对包含平均分的Map进行筛选、排序和输出
        studentAverages.entrySet()
                .stream()
                .filter(entry -> entry.getValue() >= 4.50) // 筛选平均分大于或等于4.50的学生
                .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) // 按平均分降序排序
                .forEach(pair -> {
                    // 格式化输出,平均分保留两位小数
                    System.out.printf("%s -> %.2f%n", pair.getKey(), pair.getValue());
                });

4. 完整代码示例

将以上所有部分整合,构成一个完整的Java程序:

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

public class Main {
    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--;
        }
        scanner.close();

        // 2. 预计算平均分:将学生成绩Map转换为学生平均分Map
        Map studentAverages = studentRecords.entrySet()
                .stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> entry.getValue().stream()
                                .mapToDouble(Double::doubleValue)
                                .average()
                                .orElse(0.0) // 处理无成绩的情况,尽管本例输入不会出现
                ));

        // 3. 筛选、排序并输出:
        studentAverages.entrySet()
                .stream()
                .filter(entry -> entry.getValue() >= 4.50) // 筛选平均分 >= 4.50 的学生
                .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) // 按平均分降序排序
                .forEach(pair -> {
                    System.out.printf("%s -> %.2f%n", pair.getKey(), pair.getValue()); // 格式化输出
                });
    }
}

5. 注意事项与总结

  • 性能优化: 本教程的核心优化在于将平均分计算提前,避免在 filter 和 sorted 操作中重复计算,显著提高了处理效率,尤其是在数据量较大时。
  • Stream API链式操作: Java Stream API支持链式调用,使得数据处理逻辑清晰、代码简洁。
  • Comparator 的使用: Map.Entry.comparingByValue() 结合 Comparator.reverseOrder() 是对Map的Entry进行值排序的简洁高效方式。
  • 浮点数比较: 在实际应用中,直接比较浮点数(如 average >= 4.50)可能因精度问题导致不准确。对于严格的业务逻辑,通常会引入一个小的容差值(epsilon)进行比较,例如 Math.abs(average - 4.50) 4.50 - epsilon。然而,对于本例的场景,直接比较通常足够。
  • 异常处理: 实际应用中,输入解析(如 Double.parseDouble)应考虑 NumberFormatException,并进行相应的错误处理。本例为简化起见未包含。

通过上述方法,我们能够利用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

sort排序函数用法
sort排序函数用法

sort排序函数的用法:1、对列表进行排序,默认情况下,sort函数按升序排序,因此最终输出的结果是按从小到大的顺序排列的;2、对元组进行排序,默认情况下,sort函数按元素的大小进行排序,因此最终输出的结果是按从小到大的顺序排列的;3、对字典进行排序,由于字典是无序的,因此排序后的结果仍然是原来的字典,使用一个lambda表达式作为key参数的值,用于指定排序的依据。

395

2023.09.04

php中foreach用法
php中foreach用法

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

75

2025.12.04

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

804

2023.07.31

python中的format是什么意思
python中的format是什么意思

python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

435

2024.06.27

printf用法大全
printf用法大全

php中文网为大家提供printf用法大全,以及其他printf函数的相关文章、相关下载资源以及各种相关课程,供大家免费下载体验。

75

2023.06.20

fprintf和printf的区别
fprintf和printf的区别

fprintf和printf的区别在于输出的目标不同,printf输出到标准输出流,而fprintf输出到指定的文件流。根据需要选择合适的函数来进行输出操作。更多关于fprintf和printf的相关文章详情请看本专题下面的文章。php中文网欢迎大家前来学习。

286

2023.11.28

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

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

113

2025.08.29

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

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

0

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号