本文首发于cartoon的博客

    转载请注明出处:https://cartoonyu.github.io/cartoon-blog/post/java/java%E9%81%8D%E5%8E%86%E6%9C%BA%E5%88%B6%E7%9A%84%E6%80%A7%E8%83%BD%E6%AF%94%E8%BE%83/

缘由

    近段时间在写leetcode的Lemonade Change时候,发现了for循环与forEach循环的耗时是不一致的,在提交记录上面差了一倍......

    平常开发绝大部分业务逻辑的实现都需要遍历机制的帮忙,虽说也有注意到各数据结构操作的性能比较,但是忽视了遍历机制性能的差异。原本前两天就开始动手写,拖延症......

正文

    现阶段我所知道JAVA遍历机制有三种

  • for循环

  • forEach循环

  • Iterator循环

    JAVA数据结构千千万,但是大部分都是对基础数据结构的封装,比较HashMap依赖于Node数组,LinkedList底层是链表,ArrayList对数组的再封装......扯远了

    总结来说,JAVA的基础数据结构,我觉得有两种

  • 数组
  • 链表

    如果是加上Hash(Hash的操作与数组以及链表不太一致),就是三种

    因为平常开发大部分都优先选择包装后的数据结构,所以下面我会使用

  • ArrayList(包装后的数组)
  • LinkedList(包装后的链表)
  • HashSet(包装后的Hash类型数组)

    这三种数据结构在遍历机制不同的时候时间的差异

    可能有人对我为什么不对比HashMap呢,因为JAVA设计中,是先实现了Map,再实现Set。如果你有阅读过源码就会发现:每个Set子类的实现中,都有一个序列化后的Map对应属性实现,而因为Hash的查找时间复杂度为O(1),得出key后查找value的时间大致是一致的,所以我不对比HashMap。

题外话

    我在阅读《疯狂JAVA》读到:JAVA的设计者将Map的内部entry数组中的value设为null进而实现了Set。因为我是以源码以及官方文档为准,具体我不清楚正确与否,但是因为Hash中的key互不相同,Set中元素也互不相同,所以我认为这个观点是正确的。

    为了测试的公平性,我会采取以下的限定

  • 每种数据结构的大小都设置三种量级

    • 10
    • 100
    • 1000
  • 元素都采用随机数生成
  • 遍历进行操作都为输出当前元素的值

    注:时间开销受本地环境的影响,可能测量值会出现变化,但是总体上比例是正确的

ArrayList的比较

  • 代码

    public class TextArray {
    
        private static Random random;
    
        private static List<Integer> list1;
    
        private static List<Integer> list2;
    
        private static List<Integer> list3;
    
        public static void execute(){
    random=new Random();
    initArray();
    testForWith10Object();
    testForEachWith10Object();
    testIteratorWith10Object();
    testForWith100Object();
    testForEachWith100Object();
    testIteratorWith100Object();
    testForWith1000Object();
    testForEachWith1000Object();
    testIteratorWith1000Object();
    } private static void testForWith10Object(){
    printFor(list1);
    } private static void testForWith100Object(){
    printFor(list2);
    } private static void testForWith1000Object(){
    printFor(list3);
    } private static void testForEachWith10Object(){
    printForeach(list1);
    } private static void testForEachWith100Object(){
    printForeach(list2);
    } private static void testForEachWith1000Object(){
    printForeach(list3);
    } private static void testIteratorWith10Object() {
    printIterator(list1);
    } private static void testIteratorWith100Object() {
    printIterator(list2);
    } private static void testIteratorWith1000Object() {
    printIterator(list3);
    } private static void printFor(List<Integer> list){
    System.out.println();
    System.out.print("data:");
    long start=System.currentTimeMillis();
    for(int i=0,length=list.size();i<length;i++){
    System.out.print(list.get(i)+" ");
    }
    System.out.println();
    long end=System.currentTimeMillis();
    System.out.println("for for "+list.size()+":"+(end-start)+"ms");
    } private static void printForeach(List<Integer> list){
    System.out.println();
    System.out.print("data:");
    long start=System.currentTimeMillis();
    for(int temp:list){
    System.out.print(temp+" ");
    }
    System.out.println();
    long end=System.currentTimeMillis();
    System.out.println("foreach for "+list.size()+":"+(end-start)+"ms");
    } private static void printIterator(List<Integer> list){
    System.out.println();
    System.out.print("data:");
    Iterator<Integer> it=list.iterator();
    long start=System.currentTimeMillis();
    while(it.hasNext()){
    System.out.print(it.next()+" ");
    }
    System.out.println();
    long end=System.currentTimeMillis();
    System.out.println("iterator for "+list.size()+":"+(end-start)+"ms");
    } private static void initArray(){
    list1=new ArrayList<>();
    list2=new ArrayList<>();
    list3=new ArrayList<>();
    for(int i=0;i<10;i++){
    list1.add(random.nextInt());
    }
    for(int i=0;i<100;i++){
    list2.add(random.nextInt());
    }
    for(int i=0;i<1000;i++){
    list3.add(random.nextInt());
    }
    }
    }
  • 输出(忽略对元素的输出)

    for for 10:1ms
    foreach for 10:0ms
    iterator for 10:2ms for for 100:5ms
    foreach for 100:4ms
    iterator for 100:12ms for for 1000:33ms
    foreach for 1000:7ms
    iterator for 1000:16ms
    10 100 1000
    for 1ms 5ms
    forEach 0ms 4ms
    Iterator 2ms 12ms
  • 结论

        for的性能最不稳定,foreach次之,Iterator最好

  • 使用建议

    1. 在数据量不明确的情况下(可能1w,10w或其他),建议使用Iterator进行遍历

    2. 在数据量明确且量级小的时候,优先使用foreach

    3. 需要使用索引时,使用递增变量的开销比for的要小

LinkedList的比较

  • 代码

    public class TextLinkedList {
    
        private static Random random;
    
        private static List<Integer> list1;
    
        private static List<Integer> list2;
    
        private static List<Integer> list3;
    
        public static void execute(){
    random=new Random();
    initList();
    testForWith10Object();
    testForEachWith10Object();
    testIteratorWith10Object();
    testForWith100Object();
    testForEachWith100Object();
    testIteratorWith100Object();
    testForWith1000Object();
    testForEachWith1000Object();
    testIteratorWith1000Object();
    } private static void testForWith10Object() {
    printFor(list1);
    } private static void testForEachWith10Object() {
    printForeach(list1);
    } private static void testIteratorWith10Object() {
    printIterator(list1);
    } private static void testForWith100Object() {
    printFor(list2);
    } private static void testForEachWith100Object() {
    printForeach(list2);
    } private static void testIteratorWith100Object() {
    printIterator(list2);
    } private static void testForWith1000Object() {
    printFor(list3);
    } private static void testForEachWith1000Object() {
    printForeach(list3);
    } private static void testIteratorWith1000Object() {
    printIterator(list3);
    } private static void printFor(List<Integer> list){
    System.out.println();
    System.out.print("data:");
    long start=System.currentTimeMillis();
    for(int i=0,size=list.size();i<size;i++){
    System.out.print(list.get(i));
    }
    System.out.println();
    long end=System.currentTimeMillis();
    System.out.println("for for "+list.size()+":"+(end-start)+"ms");
    } private static void printForeach(List<Integer> list){
    System.out.println();
    System.out.print("data:");
    long start=System.currentTimeMillis();
    for(int temp:list){
    System.out.print(temp+" ");
    }
    System.out.println();
    long end=System.currentTimeMillis();
    System.out.println("foreach for "+list.size()+":"+(end-start)+"ms");
    } private static void printIterator(List<Integer> list){
    System.out.println();
    System.out.print("data:");
    Iterator<Integer> it=list.iterator();
    long start=System.currentTimeMillis();
    while(it.hasNext()){
    System.out.print(it.next()+" ");
    }
    System.out.println();
    long end=System.currentTimeMillis();
    System.out.println("iterator for "+list.size()+":"+(end-start)+"ms");
    } private static void initList() {
    list1=new LinkedList<>();
    list2=new LinkedList<>();
    list3=new LinkedList<>();
    for(int i=0;i<10;i++){
    list1.add(random.nextInt());
    }
    for(int i=0;i<100;i++){
    list2.add(random.nextInt());
    }
    for(int i=0;i<1000;i++){
    list3.add(random.nextInt());
    }
    }
    }
  • 输出(忽略对元素的输出)

    for for 10:0ms
    foreach for 10:1ms
    iterator for 10:0ms for for 100:1ms
    foreach for 100:0ms
    iterator for 100:3ms for for 1000:23ms
    foreach for 1000:25ms
    iterator for 1000:4ms
    10 100 1000
    for 0ms 1ms
    forEach 1ms 0ms
    Iterator 0ms 3ms
  • 结论

        foreach的性能最不稳定,for次之,Iterator最好

  • 使用建议

    1. 尽量使用Iterator进行遍历

    2. 需要使用索引时,使用递增变量的开销比for的要小

HashSet的比较

    注:因Hash遍历算法与其他类型不一致,所以取消了for循环的比较

  • 代码

    public class TextHash {
    
        private static Random random;
    
        private static Set<Integer> set1;
    
        private static Set<Integer> set2;
    
        private static Set<Integer> set3;
    
        public static void execute(){
    random=new Random();
    initHash();
    testIteratorWith10Object();
    testForEachWith10Object();
    testIteratorWith100Object();
    testForEachWith100Object();
    testIteratorWith1000Object();
    testForEachWith1000Object();
    } private static void testIteratorWith10Object() {
    printIterator(set1);
    } private static void testForEachWith10Object() {
    printForeach(set1);
    } private static void testIteratorWith100Object() {
    printIterator(set2);
    } private static void testForEachWith100Object() {
    printForeach(set2);
    } private static void testIteratorWith1000Object() {
    printIterator(set3);
    } private static void testForEachWith1000Object() {
    printForeach(set3);
    } private static void initHash() {
    set1=new HashSet<>();
    set2=new HashSet<>();
    set3=new HashSet<>();
    for(int i=0;i<10;i++){
    set1.add(random.nextInt());
    }
    for(int i=0;i<100;i++){
    set2.add(random.nextInt());
    }
    for(int i=0;i<1000;i++){
    set3.add(random.nextInt());
    }
    } private static void printIterator(Set<Integer> data){
    System.out.println();
    System.out.print("data:");
    long start=System.currentTimeMillis();
    Iterator<Integer> it=data.iterator();
    while (it.hasNext()){
    System.out.print(it.next()+" ");
    }
    System.out.println();
    long end=System.currentTimeMillis();
    System.out.println("iterator for "+data.size()+":"+(end-start)+"ms");
    } private static void printForeach(Set<Integer> data){
    System.out.println();
    System.out.print("data:");
    long start=System.currentTimeMillis();
    for(int temp:data){
    System.out.print(temp+" ");
    }
    System.out.println();
    long end=System.currentTimeMillis();
    System.out.println("foreach for "+data.size()+":"+(end-start)+"ms");
    }
    }
  • 输出(忽略对元素的输出)

    iterator for 10:0ms
    foreach for 10:0ms iterator for 100:6ms
    foreach for 100:0ms iterator for 1000:30ms
    foreach for 1000:9ms
    10 100 1000
    foreach 0ms 0ms
    Iterator 0ms 6ms
  • 结论

        foreach性能遥遥领先于Iterator

  • 使用建议

        以后就选foreach了,性能好,写起来也方便。

总结

  1. for循环性能在三者的对比中总体落于下风,而且开销递增幅度较大。以后即使在需要使用索引时我宁愿使用递增变量也不会使用for了。
  2. Iterator的性能在数组以及链表的表现都是最好的,应该是JAVA的设计者优化过了。在响应时间敏感的情况下(例如web响应),优先考虑。
  3. foreach的性能属于两者之间,写法简单,时间不敏感的情况下我会尽量选用。

    以上就是我对常见数据结构遍历机制的一点比较,虽然只是很初步,但是从中我也学到了很多东西,希望你们也有所收获。

    如果你喜欢本文章,可以收藏阅读,如果你对我的其他文章感兴趣,欢迎到我的博客查看。

JAVA遍历机制的性能的比较的更多相关文章

  1. Java 集合 ArrayList和LinkedList的几种循环遍历方式及性能对比分析 [ 转载 ]

    Java 集合 ArrayList和LinkedList的几种循环遍历方式及性能对比分析 @author Trinea 原文链接:http://www.trinea.cn/android/arrayl ...

  2. 剑指Offer——知识点储备-故障检测、性能调优与Java类加载机制

    剑指Offer--知识点储备-故障检测.性能调优与Java类加载机制 故障检测.性能调优 用什么工具可以查出内存泄露 (1)MerroyAnalyzer:一个功能丰富的java堆转储文件分析工具,可以 ...

  3. 故障检测、性能调优与Java类加载机制

    故障检测.性能调优与Java类加载机制 故障检测.性能调优 用什么工具可以查出内存泄露 (1)MerroyAnalyzer:一个功能丰富的java堆转储文件分析工具,可以帮助你发现内存漏洞和减少内存消 ...

  4. Arraylist的遍历方式、java反射机制

    先定义ArrayList再添加几条数据:         ArrayList arr=new ArrayList(); //往arrList中增加几条数据 arr.add(1); arr.add(2) ...

  5. [转]java反射机制

    原文地址:http://www.cnblogs.com/jqyp/archive/2012/03/29/2423112.html 一.什么是反射机制         简单的来说,反射机制指的是程序在运 ...

  6. java 编程时候的性能调优

    一.避免在循环条件中使用复杂表达式 在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快. 例子: import java.util ...

  7. JAVA 画图机制

    java学习脚印:深入java绘图机制 写在前面 封装性越好的类在使用时,只要清楚接口即可,而不应该让程序员了解其内部结构; 对于平常的绘图来讲,java绘图机制无需了解太多,但是朦胧容易产生错误,绘 ...

  8. java反射机制(转)

    一.什么是反射机制         简单的来说,反射机制指的是程序在运行时能够获取自身的信息.在java中,只要给定类的名字,     那么就可以通过反射机制来获得类的所有信息. 二.哪里用到反射机制 ...

  9. Java高级开发_性能优化的细节

    一.核心部分总结: 尽量在合适的场合使用单例[减负提高效率] 尽量避免随意使用静态变量[GC] 尽量重用对象,避免过多过常地创建Java对象[最大限度地重用对象] 尽量使用final修饰符[内联(in ...

随机推荐

  1. python之mock模块基本使用

    mock简介 mock原来是python的第三方库 python3以后mock模块已经整合到了unittest测试框架中,不用再单独安装 Mock这个词在英语中有模拟的这个意思,因此我们可以猜测出这个 ...

  2. Spring Boot之Actuator的端点

    Spring Boot Actuator的关键特性是在应用程序里提供众多Web端点,通过它们了解应用程序 运行时的内部状况.有了Actuator,你可以知道Bean在Spring应用程序上下文里是如何 ...

  3. 死磕 java同步系列之CyclicBarrier源码解析——有图有真相

    问题 (1)CyclicBarrier是什么? (2)CyclicBarrier具有什么特性? (3)CyclicBarrier与CountDownLatch的对比? 简介 CyclicBarrier ...

  4. C语言实现Linux网络嗅探器

    C语言实现Linux网络嗅探器 0x01 实验简介 网络嗅探器是拦截通过网络接口流入和流出的数据的程序.所以,如果你正在浏览的互联网,嗅探器以数据包的形式抓到它并且显示.在本实验中,我们用 C 语言实 ...

  5. git的基本指令

    更多详情请看廖雪峰官方网站 http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 1.删 ...

  6. jQuery-ajax-.load方法

    使用jQuery封装的ajax是非常好用的,这个里面提供了几个比较好用的方法. load(url[,data, callback])方法: 说明:这个是jQuery中的最底层方法$.ajax()封装的 ...

  7. Codeforces Round #563 (Div. 2)C

    C. Ehab and a Special Coloring Problem 题目链接:http://codeforces.com/contest/1174/problem/C 题目 You're g ...

  8. vSphere、 ESXi、Vcenter、vSphere Client关系

    vSphere是什么? vSphere 是VMware公司发布的一整套产品包,是VMware公司推出的一套服务器虚拟化解决方案,包含VMware ESXi hypervisor,VMware vCen ...

  9. POJ 1986:Distance Queries(倍增求LCA)

    http://poj.org/problem?id=1986 题意:给出一棵n个点m条边的树,还有q个询问,求树上两点的距离. 思路:这次学了一下倍增算法求LCA.模板. dp[i][j]代表第i个点 ...

  10. Siimple DP (Dynamic Programing)

    HDU 2084:https://vjudge.net/problem/HDU-2084 Problem Describe : When it comes to the DP algorithm, a ...