zoukankan      html  css  js  c++  java
  • ArrayList和LinkedList的几种循环遍历方式及性能对比分析

    主要介绍ArrayList和LinkedList这两种list的五种循环遍历方式,各种方式的性能测试对比,根据ArrayList和LinkedList的源码实现分析性能结果,总结结论
    通过本文你可以了解(1)List的五种遍历方式及各自性能 (2)foreach及Iterator的实现 (3)加深对ArrayList和LinkedList实现的了解。
    阅读本文前希望你已经了解ArrayList顺序存储和LinkedList链式的结构,本文不对此进行介绍。

    1. List的五种遍历方式
    下面只是简单介绍各种遍历示例(以ArrayList为例),各自优劣会在本文后面进行分析给出结论。
    (1) for each循环

    (2) 显示调用集合迭代器

    (3) 下标递增循环,终止条件为每次调用size()函数比较判断

    (4) 下标递增循环,终止条件为和等于size()的临时变量比较判断

    (5) 下标递减循环

    在测试前大家可以根据对ArrayList和LinkedList数据结构及Iterator的了解,想想上面五种遍历方式哪个性能更优。

    2、List五种遍历方式的性能测试及对比
    以下是性能测试代码,会输出不同数量级大小的ArrayList和LinkedList各种遍历方式所花费的时间。

    PS:如果运行报异常in thread “main” java.lang.OutOfMemoryError: Java heap space,请将main函数里面list size的大小减小。

    其中getArrayLists函数会返回不同size的ArrayList,getLinkedLists函数会返回不同size的LinkedList。
    loopListCompare函数会分别用上面的遍历方式1-5去遍历每一个list数组(包含不同大小list)中的list。
    print开头函数为输出辅助函数。

    测试环境为Windows7 32位系统 3.2G双核CPU 4G内存,Java 7,Eclipse -Xms512m -Xmx512m
    最终测试结果如下:

    第一张表为ArrayList对比结果,第二张表为LinkedList对比结果。

    表横向为同一遍历方式不同大小list遍历的时间消耗,纵向为同一list不同遍历方式遍历的时间消耗。
    PS:由于首次遍历List会稍微多耗时一点,for each的结果稍微有点偏差,将测试代码中的几个Type顺序调换会发现,for each耗时和for iterator接近。

    3、遍历方式性能测试结果分析
    (1) foreach介绍
    foreach是Java SE5.0引入的功能很强的循环结构,for (Integer j : list)应读作for each int in list。
    for (Integer j : list)实现几乎等价于

    下面的分析会将foreach和显示调用集合迭代器两种遍历方式归类为Iterator方式,其他三种称为get方式遍历。

    这时我们已经发现foreach的一大好处,简单一行实现了四行的功能,使得代码简洁美观,另一大好处是相对于下标循环而言的,foreach不必关心下标初始值和终止值及越界等,所以不易出错Effective-Java中推荐使用此种写法遍历,本文会验证这个说法。

    使用foreach结构的类对象必须实现了Iterable接口,Java的Collection继承自此接口,List实现了Collection,这个接口仅包含一个函数,源码如下:

    iterator()用于返回一个Iterator,从foreach的等价实现中我们可以看到,会调用这个函数得到Iterator,再通过Iterator的next()得到下一个元素,hasNext()判断是否还有更多元素。Iterator源码如下:

    (2) ArrayList遍历方式结果分析

    PS:由于首次遍历List会稍微多耗时一点,for each的结果稍微有点偏差,将测试代码中的几个Type顺序调换会发现,for each耗时和for iterator接近。

    从上面我们可以看出:
    a. 在ArrayList大小为十万之前,五种遍历方式时间消耗几乎一样
    b. 在十万以后,第四、五种遍历方式快于前三种,get方式优于Iterator方式,并且

    用临时变量size取代list.size()性能更优。我们看看ArrayList中迭代器Iterator和get方法的实现

    从中可以看出get和Iterator的next函数同样通过直接定位数据获取元素,只是多了几个判断而已。

    c . 从上可以看出即便在千万大小的ArrayList中,几种遍历方式相差也不过50ms左右,且在常用的十万左右时间几乎相等,考虑foreach的优点,我们大可选用foreach这种简便方式进行遍历。

    (3) LinkedList遍历方式结果分析

    PS:由于首次遍历List会稍微多耗时一点,for each的结果稍微有点偏差,将测试代码中的几个Type顺序调换会发现,for each耗时和for iterator接近。

    从上面我们可以看出:
    a 在LinkedList大小接近一万时,get方式和Iterator方式就已经差了差不多两个数量级,十万时Iterator方式性能已经远胜于get方式。
    我们看看LinkedList中迭代器和get方法的实现

    从上面代码中可以看出LinkedList迭代器的next函数只是通过next指针快速得到下一个元素并返回。而get方法会从头遍历直到index下标,查找一个元素时间复杂度为哦O(n),遍历的时间复杂度就达到了O(n2)。

    所以对于LinkedList的遍历推荐使用foreach,避免使用get方式遍历。

    (4) ArrayList和LinkedList遍历方式结果对比分析
    从上面的数量级来看,同样是foreach循环遍历,ArrayList和LinkedList时间差不多,可将本例稍作修改加大list size会发现两者基本在一个数量级上。
    但ArrayList get函数直接定位获取的方式时间复杂度为O(1),而LinkedList的get函数时间复杂度为O(n)。
    再结合考虑空间消耗的话,建议首选ArrayList。对于个别插入删除非常多的可以使用LinkedList。

    4、结论总结
    通过上面的分析我们基本可以总结下:
    (1) 无论ArrayList还是LinkedList,遍历建议使用foreach,尤其是数据量较大时LinkedList避免使用get遍历。
    (2) List使用首选ArrayList。对于个别插入删除非常多的可以使用LinkedList。
    (3) 可能在遍历List循环内部需要使用到下标,这时综合考虑下是使用foreach和自增count还是get方式。

  • 相关阅读:
    LeetCode15 3Sum
    LeetCode10 Regular Expression Matching
    LeetCode20 Valid Parentheses
    LeetCode21 Merge Two Sorted Lists
    LeetCode13 Roman to Integer
    LeetCode12 Integer to Roman
    LeetCode11 Container With Most Water
    LeetCode19 Remove Nth Node From End of List
    LeetCode14 Longest Common Prefix
    LeetCode9 Palindrome Number
  • 原文地址:https://www.cnblogs.com/cnndevelop/p/14422202.html
Copyright © 2011-2022 走看看