【Java基礎】 -- Java遍歷List四種方法的效率對比 【轉載】

1.遍歷方法簡介

Java遍歷List的方法主要有四種:

  • for each
for(Object o :list)
{
}
  • Iterator
Iterator iter = list.iterator();
while(iter.hasNext()){
    Object o = iter.next();
}
  • loop without size
int size = list.size();
for(int i=0;i<size;i++){
    Object o= list.get(i);
}
  • loop with size
for(int i=0;i<list.size();i++){
    Object o= list.get(i);
}

注:這裏我們不比較while和for的形式,這對效率影響幾乎是可以忽略的。

 

我們是否能簡單的得出結論,哪個更快,哪個更慢呢?

嚴謹一點的方法是:基於實驗與數據,才能作出判斷。

1.1. ArrayList測試分析

經過編寫測試代碼,結果如下:(時間單位:納秒)

Size 10 100 1000 10000 100000 1000000
Foreach 448,319 558,757 732,009 2,074,092 6,169,315 15,347,540
IteratorWay 22,169 54,603 86,215 513,186 4,786,587 14,032,553
WithoutSize 14,369 32,023 158,472 828,897 3,685,905 9,457,398
WithSize 29,149 47,213 91,963 557,936 5,148,280 10,051,462

 

 可以看出,直接用循環的方法,get(index)來獲取對象,是最快的方式。而且把i<list.size()放到循環中去判斷,會影響效率。

For Each的效率最差,用迭代器的效率也沒有很好。但只是相對而言,其實從時間上看最多也就差幾毫秒。

然而,這並不是事實的全部真相!!!

上面的測試,我們只是用了ArrayList來做爲List的實現類。所以纔有上面的結論。

For each其實也是用了迭代器來實現,因此當數據量變大時,兩者的效率基本一致。也因爲用了迭代器,所以速度上受了影響。不如直接get(index)快。

那爲何get(index)會比較快呢?

因爲ArrayList是通過動態數組來實現的,支持隨機訪問,所以get(index)是很快的。迭代器,其實也是通過數組名+下標來獲取,而且增加了邏輯,自然會比get(index)慢。

看ArrayList的迭代器的源代碼就清楚了。

public boolean hasNext()
{
   return cursor != size;

}

public Object next()
{
   checkForComodification();
   int i = cursor;
   if(i >= size)
      throw new NoSuchElementException();
   Object aobj[] = elementData;
   if(i >= aobj.length)
   {
      throw new ConcurrentModificationException();
   } else
   {
       cursor = i + 1;
       return aobj[lastRet = i];
   }
}

 

1.2.LinkedList測試分析

接下來,我們用LinkedList試試,看看會產生什麼效果:(時間單位:納秒)

Size 10 100 1000 10000 100000 1000000
Foreach 542,745 388,379 952,063 2,257,196 9,426,607 12,141,976
IteratorWay 25,454 62,814 110,848 753,767 5,875,361 12,141,976
WithoutSize 27,096 95,248 3,343,097 51,302,568 3,720,958,713 692,276,304,569
WithSize 13,138 98,531 2,137,726 40,157,815 3,671,762,259 668,285,601,444

 

 結果確實不簡單,跟ArrayList完全不一樣了。

最突出的就是get(index)的方式,隨着size的增加,急劇上升。到10萬數據量時,光遍歷時間都要三四秒,這是很可怕的。

那爲何會有這樣的結果呢?還是和LinkedList的實現方式有關。

LinkedList是通過雙向鏈表實現的,無法支持隨機訪問。當你要向一個鏈表取第index個元素時,它需要二分後從某一端開始找,一個一個地數才能找到該元素。這樣一想,就能明白爲何get(index)如此費時了。

public Object get(int i)
{
    checkElementIndex(i);
    return node(i).item;
}

Node node(int i)
{
    if(i < size >> 1)
    {
        Node node1 = first;
        for(int j = 0; j < i; j++){
            node1 = node1.next;
            return node1;
        }
        Node node2 = last;
        for(int k = size - 1; k > i; k--)
            node2 = node2.prev;

        return node2;
    }
}

 

而迭代器提供的是獲取下一個的方法,時間複雜度爲O(1),所以會比較快。

public boolean hasNext()
{
    return nextIndex < size;
}

public Object next()
{
    checkForComodification();
    if(!hasNext())
    {
        throw new NoSuchElementException();
    } else
    {
        lastReturned = next;
        next = next.next;
        nextIndex++;
        return lastReturned.item;
    }
}

看這迭代器的源代碼還是很理解的。

 

2.總結

  • 對於ArrayList和LinkedList,在size小於1000時,每種方式的差距都在幾ms之間,差別不大,選擇哪個方式都可以。
  • 對於ArrayList,無論size是多大,差距都不大,選擇哪個方式都可以。
  • 對於LinkedList,當size較大時,建議使用迭代器或for-each的方式進行遍歷,否則效率會有較明顯的差距。

所以,綜合來看,建議使用for-each,代碼簡潔,性能也不差。

另外,當效率不是重點時,應該在設計上花更多心思了。實際上,把大量對象放到List裏面去,本身就應該是要考慮的問題。

至於Vector或Map,就留給感興趣的人去驗證了。

 

博文轉載自:

Java遍歷List四種方法的效率對比,感謝博主提供博文支持。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章