java流和循环 效率_Java stream 和 for循环效率对比问题

针对同一个集合,用 stream 操作两次得到两个不同条件筛选出来的集合和map,和一次for循环就搞定搞定的效率对比。

虽然stream写起来链式操作很舒服,但效率在不同数据量下的体现效果是不一样的,以下为我的测试代码:

@Test

public void testStreamAndFor() {

List studentList = new ArrayList<>();

// 初始数据量

int listSize = 100000;

// 测试次数,以便求出平均运行时长

int testTimes = 5;

for (int i = 0; i < listSize; i++) {

Student student = new Student();

student.setId(i + 1);

student.setStudentName("name" + i);

student.setAge(i);

studentList.add(student);

}

BigDecimal streamTotalRunTime = new BigDecimal("0");

BigDecimal forTotalRunTime = new BigDecimal("0");

for (int i = 0; i < testTimes; i++) {

Instant streamStart = Instant.now();

Map idMapOfStream = studentList.stream()

.collect(Collectors.toMap(Student::getId, v -> v));

List studentAgeListOfStream = studentList.stream()

.map(Student::getAge)

.collect(Collectors.toList());

long streamRunTime = Duration.between(streamStart, Instant.now()).toMillis();

System.out.println("第" + (i + 1) + "次:" + "stream 耗时:" + streamRunTime);

Instant forStart = Instant.now();

int size = studentList.size();

Map idMapOfFor = new HashMap<>(size);

List ageListOfFor = new ArrayList<>();

for (Student student : studentList) {

idMapOfFor.put(student.getId(), student);

ageListOfFor.add(student.getAge());

}

long forRunTime = Duration.between(forStart, Instant.now()).toMillis();

System.out.println("第" + (i + 1) + "次:" + "for 耗时:" + forRunTime);

streamTotalRunTime = streamTotalRunTime.add(new BigDecimal(streamRunTime + ""));

forTotalRunTime = forTotalRunTime.add(new BigDecimal(forRunTime + ""));

}

System.out.println("list长度为:" + listSize + ", 总共测试次数:" + testTimes);

System.out.println("stream总运行时间(ms) :" + streamTotalRunTime);

System.out.println("for总运行时间(ms) :" + forTotalRunTime);

BigDecimal streamAverageRunTime = streamTotalRunTime.divide(new BigDecimal(testTimes + ""), 2, BigDecimal.ROUND_HALF_UP);

System.out.println("stream平均每次运行时间(ms) :" + streamAverageRunTime);

BigDecimal forAverageRunTime = forTotalRunTime.divide(new BigDecimal(testTimes + ""), 2, BigDecimal.ROUND_HALF_UP);

System.out.println("for平均每次运行时间(ms) :" + forAverageRunTime);

}

当数据量为10w,测试5次的结果输出:

第1次:stream 耗时:81

第1次:for 耗时:13

第2次:stream 耗时:15

第2次:for 耗时:23

第3次:stream 耗时:7

第3次:for 耗时:11

第4次:stream 耗时:7

第4次:for 耗时:13

第5次:stream 耗时:9

第5次:for 耗时:6

list长度为:100000, 总共测试次数:5

stream总运行时间(ms) :119

for总运行时间(ms) :66

stream平均每次运行时间(ms) :23.80

for平均每次运行时间(ms) :13.20

当数据量为100w,测试5次的输出结果:

第1次:stream 耗时:165

第1次:for 耗时:1296

第2次:stream 耗时:447

第2次:for 耗时:62

第3次:stream 耗时:363

第3次:for 耗时:359

第4次:stream 耗时:61

第4次:for 耗时:350

第5次:stream 耗时:389

第5次:for 耗时:43

list长度为:1000000, 总共测试次数:5

stream总运行时间(ms) :1425

for总运行时间(ms) :2110

stream平均每次运行时间(ms) :285.00

for平均每次运行时间(ms) :422.00

所有运行时长单位均为ms。综上测试结果,当数据量少于百万级别的,一次for循环来筛选数据效率更高,当数据量达到八万级别,还是使用stream来操作更加具有效率。但是小弟还是有点不明白原因是为何,求高人指点一二

回答

其实用 stream 主要是很多写起来代码比较简洁,如果数据量大的话,还能很容易转换为并行流,帮你做了多线程,不需要自己代码去写多线程了

感谢老哥:+1:

我用Java11运行100W数据量的结果是二者性能差别不大, 如果堆调大一些的话, for性能更好, 说明stream需要更多的内存分配

这。。我还是用的万年java8~~老哥的这种方式,可以借鉴一手

你这代码,确定 for 和 stream 干的是同样的事情? Student::getAge 也发下看看

回复 @是码农还是程序员呢 : 我觉得你的结果很有问题,所以才需要你发出来看下,如果不发那就算了,我对你的结果存疑回复 @乌龟壳 : 代码一直都是如我所说那样操作的回复 @是码农还是程序员呢 : 你可以把代码补充到问题里,光说意义不大,很多性能问题都是细节上的问题for循环和stream都是将Student::getId放入新的map中(id为key,Student对象为value),以及将Student::getAge放入新的List中。两种方式都是对相同字段做的相同操作。

语义和使用便捷性,是优先考虑。大多数的性能问题,都不在这些suger

对于数据量和操作方式来讲,应该是有最优解,在性能差异不大的情况下肯定选择语义和使用便捷性,但性能差距过大,还是性能优先。就测试demo来看,stream筛选数据到新的数据结构(两种或多种)中,需要使用stream操作两次或多次,但使用for的话只需遍历一次就可以将原数据中所有想要的数据提取出来,所以这种情况下,是否应该要考虑for来提高效率呢

数值变化差值也太大了吧????????第一次for1200+  第五次43?

我又再一次测试了下确实如此,但我将最外层遍历(测试次数)取消掉,通过手动运行来增加测试次数后,每一次运行时长都是几十毫秒,差值并不大。所以应该是最外层遍历导致这样的差值,但这只是表面上的原因,底层原因就有点迷惑了

这测试的太草率了。

确实有一点粗糙。。大佬可有好的建议:smile:

for循环里把map的size直接就给定了,扩容要比stream少

在for循环里干了两件事,stream调用了两次

数据在10w级别,第一次调用for确实要比stream快,后续来看两者有持平的趋势

两次环境同时执行,运行环境状态不可控

可以分多次、时间线更长一点,多线程条件下在对比执行(模拟真实的应用场景)

不用测,stream内部就是for循环

不要把Stream 当做是循环,这2个并不是一回事,Stream是流,更像是一个管子,循环只是循环

就题主测试的场景来说,性能上必然是循环更快的。

Stream并不是一个循环,而是一个连接上下游的管子,比如说,我们使用Files.lines  来按行遍历一个文件的时候,他并不是一次性把文件加载到内存,解析成一个按行的集合,再进行遍历的,而是基于迭代器,每读取解析完一行数据,就发送到Stream流里,下游,map也好,flatMap也好forEach也好等等,都是每当上游发送一个数据,就处理一个数据,因为是流式的,看起来就跟遍历一起,其实这2个并不一样

那么Stream为什么能实现集合遍历呢?跟上边说的文件遍历性质是一样的,就是直接借助集合的迭代器,把集合的数据一个个发送到流里,然后下游处理,形成了看是遍历的操作,Stream处理要完成消息的传递,里面还要有其他很多辅助东西的处理,比如上游消息已经全发完了得通知到下游吧。从而就遍历上来说,Stream的性能是不可能比循环更快的

看下并行流的100W的结果,不要只是拿顺序流来测试

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值