嘿,各位 Java 开发小伙伴们!今天给大家分享一个前几天在项目中遇到的问题,这是实际项目中非常典型的性能优化案例,相信会让你对 Java 程序性能优化有更深入的理解和启发哦。让我们一起深入探索这次从性能困境到成功优化的精彩旅程吧。
一、遇到的问题:Stream 处理的性能困境
在我们的一个实际项目中遇到了这样一个需求:计算并导出全部人员全年的 考勤情况汇总数据。
接到需求之后,感觉问题不大,就是查询出相关的数据之后,循环查找和计算的问题,我们最初采用的是一种比较常见且看起来简洁的方法:使用 Stream
和 forEach 循环处理全部的数据。具体代码如下:
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
。这里,我们将NewAttendanceDetailResult
的emCode和attDate
作为键,将NewAttendanceDetailResult
对象本身作为值存储在Map
中。这个转换过程会遍历listAtt
一次,将元素存储到Map
中。
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 秒!这个结果让我们团队感到非常兴奋。
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
的高效查找特性,能够极大地减少查找元素的时间,显著提升程序性能,让程序运行得更加流畅,避免了因性能问题导致的长时间等待和用户体验下降。
启示:
希望大家从这个案例中获得启发,在自己的开发工作中更加注重性能优化。在面对性能问题时,不要害怕尝试不同的方法,深入分析和理解底层原理,找到最适合自己项目的解决方案。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有