最新最准确内容建议直接访问原文:ArrayList和LinkedList的几种循环遍历方式及性能对比分析

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

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

 

Java

 
1
2
3
4
List<Integer>list=newArrayList<Integer>();
for(Integerj:list){
    // use j
}

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

 

Java

 
1
2
3
4
List<Integer>list=newArrayList<Integer>();
for(Iterator<Integer>iterator=list.iterator();iterator.hasNext();){
    iterator.next();
}

 

Java

 
1
2
3
4
5
List<Integer>list=newArrayList<Integer>();
Iterator<Integer>iterator=list.iterator();
while(iterator.hasNext()){
    iterator.next();
}

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

 

Java

 
1
2
3
4
List<Integer>list=newArrayList<Integer>();
for(intj=0;j<list.size();j++){
    list.get(j);
}

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

 

Java

 
1
2
3
4
5
List<Integer>list=newArrayList<Integer>();
intsize=list.size();
for(intj=0;j<size;j++){
    list.get(j);
}

(5) 下标递减循环

 

Java

 
1
2
3
4
List<Integer>list=newArrayList<Integer>();
for(intj=list.size()-1;j>=0;j--){
    list.get(j);
}

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

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

ArrayList和LinkedList循环性能对比测试代码

 

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

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

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

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
compare loop performance of ArrayList
--------------------------------------------------------------
list size             |10000   |100000  |1000000|10000000
--------------------------------------------------------------
foreach              |1ms    |3ms    |14ms   |154ms  
--------------------------------------------------------------
foriterator          |0ms    |1ms    |12ms   |115ms  
--------------------------------------------------------------
forlist.size()       |1ms    |1ms    |13ms   |128ms  
--------------------------------------------------------------
forsize=list.size()|0ms    |1ms    |6ms    |62ms  
--------------------------------------------------------------
forj--               |0ms    |1ms    |7ms    |62ms  
--------------------------------------------------------------
 
compare loop performance of LinkedList
--------------------------------------------------------------
list size             |100     |1000    |10000   |100000  
--------------------------------------------------------------
foreach              |0ms    |0ms    |1ms    |3ms    
--------------------------------------------------------------
foriterator          |0ms    |0ms    |0ms    |2ms    
--------------------------------------------------------------
forlist.size()       |0ms    |0ms    |71ms   |7841ms
--------------------------------------------------------------
forsize=list.size()|1ms    |0ms    |69ms   |7874ms
--------------------------------------------------------------
forj--               |0ms    |0ms    |68ms   |7664ms
--------------------------------------------------------------

第一张表为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)实现几乎等价于

 

Java

 
1
2
3
4
Iterator<Integer>iterator=list.iterator();
while(iterator.hasNext()){
    Integerj=iterator.next();
}

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

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

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

 

Java

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
packagejava.lang;
 
importjava.util.Iterator;
 
/**
* Implementing this interface allows an object to be the target of
* the "foreach" statement.
*
* @param <T> the type of elements returned by the iterator
*
* @since 1.5
*/
publicinterfaceIterable<T>{
 
    /**
     * Returns an iterator over a set of elements of type T.
     *
     * @return an Iterator.
     */
    Iterator<T>iterator();
}

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

 

Java

 
1
2
3
4
5
6
7
publicinterfaceIterator<E>{
    booleanhasNext();
 
    Enext();
 
    voidremove();
}

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

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
compare loop performance of ArrayList
--------------------------------------------------------------
list size             |10000   |100000  |1000000|10000000
--------------------------------------------------------------
foreach              |1ms    |3ms    |14ms   |154ms  
--------------------------------------------------------------
foriterator          |0ms    |1ms    |12ms   |115ms  
--------------------------------------------------------------
forlist.size()       |1ms    |1ms    |13ms   |128ms  
--------------------------------------------------------------
forsize=list.size()|0ms    |1ms    |6ms    |62ms  
--------------------------------------------------------------
forj--               |0ms    |1ms    |7ms    |62ms  
--------------------------------------------------------------

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

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

 

Java

 
1
2
3
4
intsize=list.size();
for(intj=0;j<size;j++){
    list.get(j);
}

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

 

Java

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
privateclassItrimplementsIterator<E>{
    intcursor;       // index of next element to return
    intlastRet=-1;// index of last element returned; -1 if no such
    intexpectedModCount=modCount;
 
    publicbooleanhasNext(){
        returncursor!=size;
    }
 
    @SuppressWarnings("unchecked")
    publicEnext(){
        checkForComodification();
        inti=cursor;
        if(i>=size)
            thrownewNoSuchElementException();
        Object[]elementData=ArrayList.this.elementData;
        if(i>=elementData.length)
            thrownewConcurrentModificationException();
        cursor=i+1;
        return(E)elementData[lastRet=i];
    }
    ……
}
 
publicEget(intindex){
    rangeCheck(index);
 
    returnelementData(index);
}

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

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

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

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
compare loop performance of LinkedList
--------------------------------------------------------------
list size             |100     |1000    |10000   |100000  
--------------------------------------------------------------
foreach              |0ms    |0ms    |1ms    |3ms    
--------------------------------------------------------------
foriterator          |0ms    |0ms    |0ms    |2ms    
--------------------------------------------------------------
forlist.size()       |0ms    |0ms    |71ms   |7841ms
--------------------------------------------------------------
forsize=list.size()|1ms    |0ms    |69ms   |7874ms
--------------------------------------------------------------
forj--               |0ms    |0ms    |68ms   |7664ms
--------------------------------------------------------------

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

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

 

Java

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
privateclassListItrimplementsListIterator<E>{
    privateNode<E>lastReturned=null;
    privateNode<E>next;
    privateintnextIndex;
    privateintexpectedModCount=modCount;
 
    ListItr(intindex){
        // assert isPositionIndex(index);
        next=(index==size)?null:node(index);
        nextIndex=index;
    }
 
    publicbooleanhasNext(){
        returnnextIndex<size;
    }
 
    publicEnext(){
        checkForComodification();
        if(!hasNext())
            thrownewNoSuchElementException();
 
        lastReturned=next;
        next=next.next;
        nextIndex++;
        returnlastReturned.item;
    }
    ……
}
 
publicEget(intindex){
    checkElementIndex(index);
    returnnode(index).item;
}
 
/**
* Returns the (non-null) Node at the specified element index.
*/
Node<E>node(intindex){
    // assert isElementIndex(index);
 
    if(index<(size>>1)){
        Node<E>x=first;
        for(inti=0;i<index;i++)
            x=x.next;
        returnx;
    }else{
        Node<E>x=last;
        for(inti=size-1;i>index;i--)
            x=x.prev;
        returnx;
    }
}

从上面代码中可以看出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方式。

你可能还感兴趣:

Android性能优化系列总篇

性能优化之Java(Android)代码优化

Android公共库(缓存 下拉ListView 下载管理Pro 静默安装 root运行 Java公共类)

Android ImageCache图片缓存

ArrayList和LinkedList的几种循环遍历方式及性能对比分析的更多相关文章

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

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

  2. ArrayList和LinkedList的几种循环遍历方式及性能对比分析(转)

    主要介绍ArrayList和LinkedList这两种list的五种循环遍历方式,各种方式的性能测试对比,根据ArrayList和LinkedList的源码实现分析性能结果,总结结论. 通过本文你可以 ...

  3. ArrayList和LinkedList的几种循环遍历方式及性能对比分析(转载)

    原文地址: http://www.trinea.cn/android/arraylist-linkedlist-loop-performance/ 原文地址: http://www.trinea.cn ...

  4. 【转】ArrayList和LinkedList的几种循环遍历方式及性能对比分析

    原文网址:http://www.trinea.cn/android/arraylist-linkedlist-loop-performance/ 主要介绍ArrayList和LinkedList这两种 ...

  5. (转)ArrayList和LinkedList的几种循环遍历方式及性能对比分析

    主要介绍ArrayList和LinkedList这两种list的五种循环遍历方式,各种方式的性能测试对比,根据ArrayList和LinkedList的源码实现分析性能结果,总结结论. 通过本文你可以 ...

  6. ArrayList和LinkedList遍历方式及性能对比分析

    ArrayList和LinkedList的几种循环遍历方式及性能对比分析 主要介绍ArrayList和LinkedList这两种list的五种循环遍历方式,各种方式的性能测试对比,根据ArrayLis ...

  7. HashMap循环遍历方式及其性能对比(zhuan)

    http://www.trinea.cn/android/hashmap-loop-performance/ ********************************************* ...

  8. HashMap循环遍历方式及其性能对比

    主要介绍HashMap的四种循环遍历方式,各种方式的性能测试对比,根据HashMap的源码实现分析性能结果,总结结论.   1. Map的四种遍历方式 下面只是简单介绍各种遍历示例(以HashMap为 ...

  9. ArrayList与LinkedList的普通for循环遍历

    对于大部分Java程序员朋友们来说,可能平时使用得最多的List就是ArrayList,对于ArrayList的遍历,一般用如下写法: public static void main(String[] ...

随机推荐

  1. nyoj163_Phone List_字典树

    Phone List 时间限制:1000 ms  |  内存限制:65535 KB 难度:4   描述 Given a list of phone numbers, determine if it i ...

  2. hihocoder 1061.Beautiful String

    题目链接:http://hihocoder.com/problemset/problem/1061 题目意思:给出一个不超过10MB长度的字符串,判断是否里面含有一个beautiful strings ...

  3. CSS3实现元素旋转

    -webkit-transform:rotate(30deg); 参数代表顺时针自旋转角度 这个元素还提供了文本倾斜的方法 - HTML5与CSS3 P299

  4. Android图片加载库:最全面的Picasso讲解

    前言 上文已经对当今 Android主流的图片加载库 进行了全面介绍 & 对比 如果你还没阅读,我建议你先移步这里阅读 今天我们来学习其中一个Android主流的图片加载库的使用 - Pica ...

  5. iOS block 的底层实现

    其实swift 的闭包跟 OC的block 是一样一样的,学会了block,你swift里边的闭包就会无师自通. 参考:http://www.jianshu.com/p/e23078c11518 ht ...

  6. python安装paramiko模块

    一.简介 paramiko是用python语言写的一个模块,遵循SSH2协议,支持以加密和认证的方式,进行远程服务器的连接. 由于使用的是python这样的能够跨平台运行的语言,所以所有python支 ...

  7. CSS实现限制显示的字数,超出显示"..."

    一.背景 在实际项目中,我们常常需要对某些页面的某些特定区域显示指定数量的内容,超出的内容显示"..."来进行美化页面,那么应该怎么做呢?今天就让我们来看看如何达到这一效果. 二. ...

  8. Spetember 5th 2016 Week 37th Monday

    No matter how far you may fly, never forget where you come from. 无论你能飞多远,都别忘了你来自何方. Stay true to you ...

  9. Linux Window Redis安装

    Linux 下简易的安装过程: 1.源码安装之前要先安装gcc,不然编译会出错, 2.下载源码,去http://download.redis.io/releases/这个文件夹内找想安装的版本,我下载 ...

  10. 在Linux中安装JDK的步骤

    相信不少学习Java的朋友都在Windows操作系统中安装过JDK,这里就不对JDK做详细的介绍了. 在Windows下安装JDK可参考:JDK的安装和配置 1.下载JDK 我们可以去官网(http: ...