1. 程式人生 > >Java中遍歷list四種方法效率比較

Java中遍歷list四種方法效率比較

Java遍歷List四種方法的效率對比

遍歷方法簡介

Java遍歷List的方法主要有:

(1)for each

for(bject o :list)

   {

   }

[java] view plain copy print?

(2)Iterator

Iterator iter = list.iterator();

   while(iter.hasNext()){

      Object o = iter.next();

   }

(3)loop without size

int size = list.size();

   for(int i=0;i<size

;i++){

      Object o= list.get(i);

   }

(4)loop with size

for(int i=0;i<list.size();i++){

      Object o= list.get(i);

   }

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

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

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

ArrayList測試分析

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

Size

10

100

1,000

10,000

100,000

1,000,000

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];

       }

   }

LinkedList測試分析

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

Size

10

100

1,000

10,000

100,000

1,000,000

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;

    }

}

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

總結

(1)對於ArrayList和LinkedList,在size小於1000時,每種方式的差距都在幾ms之間,差別不大,選擇哪個方式都可以。

(2)對於ArrayList,無論size是多大,差距都不大,選擇哪個方式都可以。

(3)對於LinkedList,當size較大時,建議使用迭代器或for-each的方式進行遍歷,否則效率會有較明顯的差距。

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

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

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

系統資訊

最後,附上系統資訊:

– listing properties –

java.vm.version=25.65-b01

java.vm.name=Java HotSpot(TM) 64-BitServer VM

java.runtime.version=1.8.0_65-b17

os.arch=amd64

os.name=Windows 10

java version”1.8.0_66”

Java(TM) SERuntime Environment (build 1.8.0_66-b18)

JavaHotSpot(TM) Client VM (build 25.66-b18, mixed mode)