Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Java 性能优化 | 神操作:从 200 秒到 300 毫秒,性能大逆袭!

Java 性能优化 | 神操作:从 200 秒到 300 毫秒,性能大逆袭!

作者头像
架构师精进
发布于 2025-04-04 11:32:18
发布于 2025-04-04 11:32:18
16200
代码可运行
举报
文章被收录于专栏:公众号文章公众号文章
运行总次数:0
代码可运行

嘿,各位 Java 开发小伙伴们!今天给大家分享一个前几天在项目中遇到的问题,这是实际项目中非常典型的性能优化案例,相信会让你对 Java 程序性能优化有更深入的理解和启发哦。让我们一起深入探索这次从性能困境到成功优化的精彩旅程吧。

一、遇到的问题:Stream 处理的性能困境

在我们的一个实际项目中遇到了这样一个需求:计算并导出全部人员全年的 考勤情况汇总数据。

接到需求之后,感觉问题不大,就是查询出相关的数据之后,循环查找和计算的问题,我们最初采用的是一种比较常见且看起来简洁的方法:使用 Stream 和 forEach 循环处理全部的数据。具体代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
finalSummaryList.stream().parallel().forEach(summary -> {
    cycle.stream().forEach(c -> {
        // 3.2、根据 List<NewAttendanceDetailResult> list计算年事假,病假,旷工,情况,并赋值给AttendStaffLeaveSummary对象中的kuangGong1 到kuangGong12字段,病假,事假也是如此
        String key = summary.getEmCode() + "-"+ cycle;
        //计算采用的数据源
        boolean flag = calculateOldOrNewRecord(cycle,summary.getIspaiban());
        if (flag){
             NewAttendanceDetailResult record = listAtt.stream()
                .filter(r -> summary.getEmCode().equals(r.getEmCode()) && attdate.getMonth() ==r.getAttdate().getMonth())
                .findFirst().orElse(null);

            calculateLeaveFromNewAttendanceDetailResult(record,summary);
        } else {
             AttendanceRecord oldRecord = listOld.stream()
                .filter(r -> summary.getEmCode().equals(r.getEmployeenum())  && attdate.getMonth()==r.getAttdate().getMonth())
                .findFirst().orElse(null);

            calculateLeaveFromAttendanceRecord(oldRecord,summary);
        }
        System.out.println("summary complete," + summary.getEmCode());

    });
});

在上述代码中,使用了 Java 8 的流(Stream)和 Lambda 表达式,主要功能是处理 finalSummaryList 中的元素,并且使用并行流(parallel())进行处理,目的可能是为了提高处理速度。对于 finalSummaryList 中的每个元素 summary,会遍历 cycle 中的元素 c,并执行一系列操作。

二、性能表现:耗时到达200s

这种使用 Java 8 的函数式编程特性的方式,虽然简洁明了,具有很高的可读性。然而,理想很美好,现实却很残酷。当我们在处理大量数据时,这个程序的性能表现却不尽如人意。在实际运行中,我们发现整个数据导出过程异常缓慢,经过仔细的测试和计时,这个过程居然需要 180 秒以上的时间!这严重影响了我们系统的整体性能,用户在等待数据导出时可能会遭遇长时间的延迟,导致用户体验极差,甚至可能影响业务的正常开展。

三、问题分析:Stream为什么效率低

深入分析这个性能问题,我们发现其核心问题在于 Stream流处理的特性。在这个for循环中,每次迭代都会创建一个新的Stream流,并且对数据集进行filter操作。由于filter操作需要遍历所有的元素,其时间复杂度为。想象一下,当 listAtt 数据集包含大量元素,而finalSummaryList也有众多元素时,我们就会陷入一个恶性循环:在每次for循环迭代中,都需要对listAtt 进行多次遍历和筛选操作,这就像在一个庞大的图书馆里,每次找一本书都要重新把书架上的书一本本检查一遍,效率自然低下。这种重复的高时间复杂度操作,随着数据量的增加,性能开销会呈指数级增长,最终导致程序像蜗牛一样慢,极大地影响了系统的响应速度和用户体验。

四、优化解决:转向 Map 查找的方案

为了克服这个性能瓶颈,我们开始了一系列的优化探索。经过多次尝试和测试,最终发现将 listAtt 转换为 Map 并使用 get(key) 进行查找的方式效果非常显著。

我们首先使用 stream().collect(Collectors.toMap(...))方法将数据集转换为Map。这里,我们将NewAttendanceDetailResultemCode和attDate作为键,将NewAttendanceDetailResult对象本身作为值存储在Map中。这个转换过程会遍历listAtt一次,将元素存储到Map 中。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Map<String,NewAttendanceDetailResult> listNewResult =listAtt.stream()
// 提取每个对象的键(由emCode和attdate的年月组成)以及对应的对象本身
.collect(Collectors.toMap(
        att -> {
            String emCode = att.getEmCode();
            Date attdate =  att.getAttdate();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
            String attdateStr = sdf.format(attdate);
            return emCode + "-" + attdateStr;
        },
        att -> att
));

然后,在 for 循环中,我们通过 get(key) 方法直接从 Map 中查找元素。当我们完成这次优化并运行程序时,效果立竿见影,数据处理时间从之前的 200 秒以上迅速缩减到了仅仅 1 到 2 秒!这个结果让我们团队感到非常兴奋。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
finalSummaryList.stream().parallel().forEach(summary -> {
    cycle.stream().forEach(c -> {
        // 3.2、根据 List<NewAttendanceDetailResult> list计算年事假,病假,旷工,情况,并赋值给AttendStaffLeaveSummary对象中的kuangGong1 到kuangGong12字段,病假,事假也是如此
        String key = summary.getEmCode() + "-"+ cycle;
        //计采用的数据源
        boolean flag = CalOldOrNewRecord(cycle,summary.getIspaiban());
        if (flag){
            NewAttendanceDetailResult record = listNewResult.get(key);
            calculateLeaveFromNewAttendanceDetailResult(record,summary);
        } else {
            AttendanceRecord oldRecord= list.get(key);
            calculateLeaveFromAttendanceRecord(oldRecord,summary);
        }
        System.out.println("summary complete," + summary.getEmCode());
    });
});

优化后的结果:

五、优化原理:深入剖析性能提升的秘密

那么,为什么会有如此显著的性能提升呢 让我们来深入剖析一下其中的原理吧。

Stream 处理的性能分析

在使用Stream处理时,每次for循环迭代都会创建一个新的Stream实例,并且在filter操作中,它需要遍历List中的元素。这个filter操作实际上是在执行一个条件判断来筛选元素,对于List中的每个元素都要进行这样的判断,这意味着时间复杂度是一个

的操作。而且,由于for循环的存在,对于List中的每个元素,都会触发这样一个的查找过程,这是一个嵌套的时间复杂度问题,整体性能会变得非常糟糕,特别是当数据量较大时,计算量会成倍增加。

Map 查找的性能优势

六、总结与启示:性能优化的思考与实践经验

通过这次性能优化的实践,我们获得了很多宝贵的经验。

  • 在处理小数据集时,使用 Stream 流处理可以使代码更加简洁和具有可读性,并且由于数据量小,性能损失并不明显,是一个不错的选择。它能够让我们以一种函数式的编程风格来表达复杂的数据处理逻辑,提高代码的可维护性和开发效率。
  • 然而,当面对大数据集,尤其是在 for 循环中需要频繁查找元素时,将列表转换为 Map 进行查找是一种更优的策略。这种方式利用了 Map 的高效查找特性,能够极大地减少查找元素的时间,显著提升程序性能,让程序运行得更加流畅,避免了因性能问题导致的长时间等待和用户体验下降。

启示

  • 在开发过程中,我们不能仅仅满足于代码的简洁性和功能性,还需要考虑性能因素。对于不同的数据处理场景,要根据数据量和操作频率等因素,灵活选择合适的数据结构和算法。
  • 性能优化往往需要我们深入理解数据结构和操作的时间复杂度,分析代码的执行过程,找到性能瓶颈所在,而不是仅仅依赖于表面上的代码简洁性。有时候,看似简单的代码可能隐藏着性能陷阱,而一些看起来稍微复杂一点的优化方案,可能会带来意想不到的性能提升。

希望大家从这个案例中获得启发,在自己的开发工作中更加注重性能优化。在面对性能问题时,不要害怕尝试不同的方法,深入分析和理解底层原理,找到最适合自己项目的解决方案。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-01-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 架构师精进 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
MongoDB数据库的基本使用总结
江湖有缘
2023/09/14
1.5K0
MongoDB数据库的基本使用总结
MongoDB 部署
MongoDB(来自于英文单词“Humongous”,中文含义为“庞大”)是可以应用于各种规模的企业、各个行业以及 各类应用程序的开源数据库。作为一个适用于敏捷开发的数据库,MongoDB的数据模式可以随着应用程序的发 展而灵活地更新。与此同时,它也为开发人员 提供了传统数据库的功能:二级索引,完整的查询系统以及严格 一致性等等。 MongoDB能够使企业更加具有敏捷性和可扩展性,各种规模的企业都可以通过使用MongoDB来 创建新的应用,提高与客户之间的工作效率,加快产品上市时间,以及降低企业成本。
小手冰凉
2020/07/28
1.3K0
MongoDB 部署
听说MongoDB你很豪横?-------------MongoDB数据库基础详解
传统的关系型数据库(如MySQL) ,在数据操作的"三高需求以及应对Web2.0的网站需求面前,显得力不从心。 解释:“三高”需求: ●High performance -对数据库并发读写的需求。 ●Huge Storage -对海量数据的高效率存储和访问的需求。 ●High Scalability & High Availability-对数据库的高可扩展性和高可用性的需求。
不吃小白菜
2020/09/15
1.1K0
MongoDB 使用系列(一)-- 安装
环境 系统:Ubuntu 16.04 MongoDB 版本:3.6 安装 添加软件源 1.添加 MongoDB 签名到 APT $ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927 2.创建/etc/apt/sources.list.d/mongodb-org-3.6.list文件并写入命令 Ubuntu 14.04 $ echo "deb [ arch=amd64 ] https://repo.m
木制robot
2018/04/13
1.3K0
MongoDb简介
知识点名 "什么是MongoDB ? MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。 分布式系统 分布式系统(distributed system)由多台计算机和通
爱喝水的木子
2022/01/12
3.8K0
MongoDB的备份与恢复
1.1 MongoDB的常用命令 mongoexport / mongoimport mongodump / mongorestore      有以上两组命令在备份与恢复中进行使用。 1.1.1 导出工具mongoexport Mongodb中的mongoexport工具可以把一个collection导出成JSON格式或CSV格式的文件。可以通过参数指定导出的数据项,也可以根据指定的条件导出数据。    该命令的参数如下: 参数 参数说明 -h 指明数据库宿主机的IP
惨绿少年
2018/03/30
4.5K0
MongoDB入门与安装 [纯技术]
mongodb是一个nosql数据库,所有的数据都是以bson格式去存储在数据库里面的,什么是bson呢,bson是一种比json更强的数据存储格式,如果你是小白,可以直接看做json
Jean
2018/10/11
4410
Mongodb基本操作
# /usr/local/mongodb/bin/mongod --dbpath=/usr/local/mongodb/data/ --fork --logpath=/usr/local/mongodb/log/mongodb.log --auth
Lansonli
2021/10/09
4620
MongoDB初了解——用户权限
本文所述MongoDB版本为4.0.5,笔者对MongoDB刚接触,对各个版本的MongoDB不甚了解,本文不对该版本的MongoDB做特性介绍,所涉及命令也许对其余版本不适用。
用户1148394
2019/01/07
1.1K0
Linux 安装 MongoDB
一、下载 Linux:CentOS 7.3 64位 MongoDB:3.6.4 安装目录:/usr/local cd /usr/local wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel62-3.6.4.tgz 回到顶部 二、解压缩     解压缩安装包并重命名(方便管理) tar -zxvf mongodb-linux-x86_64-rhel62-3.6.4.tgz mv mongodb-linux-x86_64-rhel62
JMCui
2018/06/14
2.1K0
MongoDB基础
MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。在高负载的情况下,添加更多的节点,可以保证服务器性能。MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。在nosql数据库里,大部分的查询都是键值对(key、value)的方式。MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中最像关系数据库的。其特征NoSQL、文档存储、Json数据模型、支持事务。
KaliArch
2018/05/30
1.7K2
MongoDB基础
CentOS 7下MongoDB 3.6 的安装及基本操作
1.MongoDB是一款跨平台、面向文档的数据库,可以实现高性能,高可用性,并且能够轻松扩展。MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。 在高负载的情况下,添加更多的节点,可以保证服务器性能。MongoDB可以为Web应用提供可扩展的高性能数据存储解决方案。
星哥玩云
2022/08/17
1.1K0
CentOS 7下MongoDB 3.6 的安装及基本操作
MongoDB副本(一主一备+仲裁)环境部署记录
MongoDB复制集是一个带有故障转移的主从集群。是从现有的主从模式演变而来,增加了自动故障转移和节点成员自动恢复。 MongoDB复制集模式中没有固定的主结点,在启动后,多个服务节点间将自动选举产生一个主结点。该主结点被称为primary,一个或多个从结点被称为secondaries。 primary结点基本上就是master结点,不同之处在于primary结点在不同时间可能是不同的服务器。如果当前的主结点失效了,复制集中的其余结点将会试图选出一个 新的主结点。 MongoDB复制集模式的好处: 一切自动
洗尽了浮华
2018/01/23
3K1
MongoDB副本(一主一备+仲裁)环境部署记录
mongodb主从配置及备份
本文将介绍下mongodb主从配置及备份 ---- MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。 MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。 主从服务器的实现原理 首先,主节点会把本服务的与写有关的操作记录下来,读操来不记录,这些操作就记录在local数据库中的oplog.$admin这个集合中,这是一个固定集合,大小是可以配置的,主要是通过配
程序员同行者
2018/07/02
1.6K0
Linux下的Mongodb部署应用梳理
一、Mongodb简介  官网地址:http://www.mongodb.org/ MongoDB是一个高性能,开源,无模式的文档型数据库,是当前NoSql数据库中比较热门的一种。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能 最丰富,最像关系数据库的。它在许多场景下可用于替代传统的关系型数据库或键/值存储方式。它是由C++语言编写的一个基于分布式文件存储的开源数据库系统,它的目的在于为WEB应 用提供可扩展的高性能数据存储解决方案。MongoDB是一个介于关系型数据
洗尽了浮华
2018/01/22
5.1K0
Linux下的Mongodb部署应用梳理
MongoDB运维与开发(一)
工作方向上的原因,不得不接触部分MongoDB的运维工作,之前有接触过一些MongoDB的内容,基本的运维操作没有什么问题,包括MongoDB的集群搭建、数据分片功能等都测试过。但是时间久了,很多东西不用就忘记了,最近准备出一个系列的MongoDB的运维操作文章,希望把这块儿内容重新拾起来。网上查了查,MongDB讲得好的书也就是<MongoDB权威指南>这本了,但是它引用的MongoDB版本比较旧,所以最好结合着官方文档看,这样收获会更快。MongoDB中文论坛里面也有不少前人总结的好文档,对学习都很有帮助。废话不多说,开始讲述吧!
AsiaYe
2020/11/02
1.4K0
MongoDB运维与开发(三)
今天来看MongoDB的用户相关的内容,用户、权限,这块儿的内容还是比较多的。慢慢来看
AsiaYe
2020/11/03
1.8K0
《MongoDB极简教程》第一章 NoSQL简史 & MongoDB安装&环境配置NoSQLNoSQL 简史CAP定理(CAP theorem)BASEMongoDB 特性&优势文档参考安装&环境配置
MongoDB 是一款开源的文档数据库,并且是业内领先的 NoSQL 数据库,用 C++ 编写而成。
一个会写诗的程序员
2018/08/20
1K0
《MongoDB极简教程》第一章 NoSQL简史 & MongoDB安装&环境配置NoSQLNoSQL 简史CAP定理(CAP theorem)BASEMongoDB 特性&优势文档参考安装&环境配置
Mongodb主从搭建
Mongodb主从搭建 内存2以上 无特殊要求 主IP:192.168.1.100 从IP:192.168.1.101 准备配置如下,每台服务器都执行 sudo echo "never" > /sys/kernel/mm/transparent_hugepage/enabled sudo echo "never" > /sys/kernel/mm/transparent_hugepage/defrag vim /etc/security/limits.conf # 添加mongo用户可以打开的文件数量的
Yuou
2022/09/26
5650
MongoDB快速入门,掌握这些刚刚好!(第一篇)
之后在admin集合中创建一个账号用于连接,这里创建的是基于root角色的超级管理员帐号;整个账号创建过程可以参考下:
用户10002156
2023/12/19
1460
MongoDB快速入门,掌握这些刚刚好!(第一篇)
相关推荐
MongoDB数据库的基本使用总结
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验