for-forEach-stream三种遍历方法执行效率比较与选用思考

Posted 张侦毅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了for-forEach-stream三种遍历方法执行效率比较与选用思考相关的知识,希望对你有一定的参考价值。

文章目录

想法

  在JDK-8中,新添加了很多的方法,比如说现在我所说的forEach,该方法是用于集合遍历的,其方式相当于传统的for循环遍历方式,只是与其不同之处就在于采用了lambda表达式,因而在进行循环遍历时代码更加的简介。

  但是我们知道,在JDK-8中,除了可以采用forEach来进行集合遍历之外,还可以采用流的形式来对集合进行遍历操作,比如说stream,因而我就突然想到了比较一下三者执行效率到底如何。

设计

  下面就是我对于该一想法的实现,由于以上三种方法中含有流,所以说我们操作的对象就选择为实体类,因而我创建了一个实体类Demo,在该实体类中一共存放了两个字段,分别为index索引值字段以及str字符串字段。其中index索引值字段存放的是我们要取用的信息,而str字段则是属于干扰字段,实际上并没有取用。接下来我就是创建100万个Demo,在其中存放相应的信息,然后再使用不同的方法对其进行遍历。最后一步就是比较for、forEach与stream中两两执行时间差异并由此得出他们的执行效率排序。

实现

  Demo中的源码:

package com.lyc;

import lombok.*;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "index")
public class Demo 

    private int index;
    private String str;


  TraverseComparisonTest中的源码:

package com.lyc;

import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
public class TraverseComparisonTest 

    List<Demo> demoList = null;
    List<Integer> traditionList = null;
    List<Integer> forEachList = null;
    List<Integer> streamList = null;

    /**
     * 准备数据
     */
    @Before
    public void prepareDatabase()
        demoList = Lists.newArrayList();
        traditionList = Lists.newArrayList();
        forEachList = Lists.newArrayList();
        streamList = Lists.newArrayList();
        String str = "这个是干扰字段;这个是干扰字段;这个是干扰字段;这个是干扰字段;这个是干扰字段;" +
                "这个是干扰字段;这个是干扰字段;这个是干扰字段;这个是干扰字段;这个是干扰字段;这个是干扰字段;" +
                "这个是干扰字段;这个是干扰字段;这个是干扰字段;这个是干扰字段;这个是干扰字段;这个是干扰字段;" +
                "这个是干扰字段;这个是干扰字段;这个是干扰字段;";
        for(int i = 0;i < 1000000;i ++)
            Demo demo = new Demo(i,str);
            demoList.add(demo);
        
    

    /**
     * 传统的方式
     * @return
     */
    public long traditionMethod()
        Date date = new Date();
        int traditionListLength = traditionList.size();
        for(int i = 0;i < traditionListLength;i ++)
            traditionList.add(demoList.get(i).getIndex());
        
        Date date2 = new Date();
        return date2.getTime() - date.getTime();
    

    /**
     * forEach方式
     * @return
     */
    public long forEachMethod()
        Date date = new Date();
        demoList.forEach(demo -> 
            forEachList.add(demo.getIndex());
        );
        Date date2 = new Date();
        return date2.getTime() - date.getTime();
    

    /**
     * stream方式
     * @return
     */
    public long streamMethod()
        Date date = new Date();
        streamList = demoList.stream().map(demo -> demo.getIndex()).collect(Collectors.toList());
        Date date2 = new Date();
        return date2.getTime() - date.getTime();
    

    /**
     * 比较stream与传统的取值方式效率
     */
    @Test
    public void test()
        long time1 = traditionMethod();
        long time2 = forEachMethod();
        long time3 = streamMethod();
        log.info("for与forEach比较结果为:" + (time1 - time2));
        log.info("forEach与stream比较结果为:" + (time2 - time3));
        log.info("for与stream比较结果为:" + (time1 - time3));
    


  执行结果:

2018-03-03 15:20:14  INFO main com.lyc.TraverseComparisonTest.test(TraverseComparisonTest.java:85) - for与forEach比较结果为:-78
2018-03-03 15:20:14  INFO main com.lyc.TraverseComparisonTest.test(TraverseComparisonTest.java:86) - forEach与stream比较结果为:-678
2018-03-03 15:20:14  INFO main com.lyc.TraverseComparisonTest.test(TraverseComparisonTest.java:87) - for与stream比较结果为:-756

结论

  通过上述比较,在共同对Demo遍历100万次的过程中,for循环比forEach快78毫秒,而forEach又比stream快678毫秒,而for比stream快756毫秒。参考代码量与执行效率,我们不难得出如下结论:

  • 它们的执行效率排序为 :for > forEach > stream
  • 它们的代码编写量排序为:for < forEach < stream

建议

  在编写代码的过程中,我们究竟采用哪种遍历方式比较好?!这个得有一个选择标准,如果单纯的以运行效率为主,那还是采用传统的for循环,若要简化代码量,那就选用stream。不过我给大家的建议是多使用stream而不是运行效率最高的for循环,理由如下:

  回顾编程的发展历史,我们不难发现一个规律,那就是先是从最初的C/C++演变到Java/.net,这是编程界的一大进步,因为我们不再关注于指针操作,比如在java中JVM虚拟机已经帮我们完成了相应的操作,由于这一进步,这付出的代价是执行效率会降低,但是带来的好处就在于加快了编程开发的速度。

  当编程由Java/.net演变到javascript/php/Kotlin,这又是编程界的另一大进步,这意味着我们在编写程序时没有必要再关注于数据类型,而该数据类型是由相应的语言在运行时确定,这样,这又一次降低了程序的运行速度,但是相应的又提升了代码编写的效率,因而通过回顾历史我们不难得出如下结论:

  在编写代码时,一定要以最简洁为原则,毕竟运行程序的硬件成本会随着时间的推移在不断降低,而程序员的薪资则不会。

  最后需要说明的是,感谢大家的批评与指导,在此我发现自己知识水平的不足。在以上的测试场景中,我并没有考虑到高并发的应用场景,对于详细的Stream应用知识,推荐大家观看如下文章:

源码:for-foreach-stream-parent


以上是关于for-forEach-stream三种遍历方法执行效率比较与选用思考的主要内容,如果未能解决你的问题,请参考以下文章

python 三种遍历list的方法

二叉树三种深度遍历方法和实现

第六十九课 二叉树的线索化实现

ArrayList三种遍历方法

遍历List集合的三种方法

遍历List集合的三种方法