java8 为stream().forEach效率正名

首先先说明,这篇文章的对比有失公允,stream()和parallelStream()差距还是挺大的,用parallelStream()这种开线程的玩意和单线程比较,有点不公平。

Stream流是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身并不存储任何元素(或其地址值),它只是在原数据集上定义了一组操作。

Stream流不保存数据,Stream操作是尽可能惰性的,即每当访问到流中的一个元素,才会在此元素上执行这一系列操作。

Stream流也不会改变原有数据。

具体Stream流的知识请大家自行搜索,仅仅以循环效率来考虑,确实有点一叶障目。

以下内容大家看看就好:

————————————————————————————————————————————

java8新出的循环方式,在网上有大量的道友说用流的方式效率反而更低了。

大量的结论表明,这种方式只是语法糖(for-forEach-stream三种遍历方法执行效率比较与选用思考 - ZZY1078689276的专栏 - CSDN博客
https://blog.csdn.net/ZZY1078689276/article/details/79430772
)这篇文章一样的内容看了不止一次,我都不知道谁先写的。。

不过,耳听为虚,眼见为实,上代码亲自试验:

public static void main(String[] args) {
    // 测试源
    List<String> sourceList = new ArrayList<>();
    for (int i = 0;i<10;i++) {
        sourceList.add("第" + i + "条数据");
    }

    System.out.println("数据条数:" + sourceList.size());
    long a1=System.currentTimeMillis();
    for (int i = 0;i < sourceList.size();i++) doSome();
    long a2=System.currentTimeMillis();
    System.out.println("普通for循环用时:" + (a2-a1));

    long b1=System.currentTimeMillis();
    for (String t:sourceList) doSome();
    long b2=System.currentTimeMillis();
    System.out.println("增强for循环用时:" + (b2-b1));

    long c1=System.currentTimeMillis();
    sourceList.forEach((t)-> doSome());
    long c2=System.currentTimeMillis();
    System.out.println("forEach循环用时:" + (c2-c1));

    long d1=System.currentTimeMillis();
    sourceList.parallelStream().forEach((t)-> doSome());
    long d2=System.currentTimeMillis();
    System.out.println("forEach-Stream循环用时:" + (d2-d1));
}

结果如下:

数据条数:10
普通for循环用时:0
增强for循环用时:0
forEach循环用时:43
forEach-Stream循环用时:6 

好吧,数据少了点,不过结果也很明显了,使用forEach循环耗时最多,流启动也很耗时。

不过,让我们把循环次数提升至100W如何?

数据条数:1000000
普通for循环用时:3
增强for循环用时:10
forEach循环用时:46
forEach-Stream循环用时:33

forEach-Stream方式用时增加了,不过forEach用时并没有明显增加,再来一次:

数据条数:1000000
普通for循环用时:3
增强for循环用时:7
forEach循环用时:38
forEach-Stream循环用时:31

exm?forEach循环用时反而减少了是什么情况?不过今天我不研究原理,只看结论的话,for循环和增强for循环至少还是比新出的forEach-Stream要强很多的。

但这么一来,难道新功能真的只是语法糖么?

有人问我doSome()做了什么?实际上我什么都没做,但如果我真的做了什么呢?

比如等待一毫秒?

private static void doSome() {
    try {
        Thread.sleep(1);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

再来一次:

 数据条数:10000
普通for循环用时:10025
增强for循环用时:10009
forEach循环用时:10067
forEach-Stream循环用时:1567

Duang!!!!!!!

forEach-Stream的效率获得了碾压式的胜利!有人说是因为jvm预热原因,但先且不论预热的是什么(通常是指启动时加载的东西),预热这种一次性的行为怎么可能取得这么大的提升?

于是,让我们打印一下doSome()的线程

 

ForkJoinPool.commonPool-worker-5
ForkJoinPool.commonPool-worker-4
ForkJoinPool.commonPool-worker-6
ForkJoinPool.commonPool-worker-2
main
ForkJoinPool.commonPool-worker-3
ForkJoinPool.commonPool-worker-7
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-6
main
ForkJoinPool.commonPool-worker-2
ForkJoinPool.commonPool-worker-3
ForkJoinPool.commonPool-worker-4
ForkJoinPool.commonPool-worker-5
ForkJoinPool.commonPool-worker-7
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-5
main

So,还用我解释什么么?

 

stream().forEach用的多线程方式,其调用线程池的时候必然会耗费更多的时间。但如果你在循环内要处理的事情很多,或者要循环调用远程接口/数据库的时候,无疑极大的提升了效率

 

恭喜你学会了最简洁的多线程编程!

评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SarielAngel

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值