什么情況下ArrayList增刪 比LinkedList 更快



public
static void main(String[] args){ final int MAX_VAL = 10000; List<Integer> linkedList = new LinkedList<Integer>(); List<Integer> arrayList = new ArrayList<Integer>(); for(int i = 0; i < MAX_VAL; i++) { linkedList.add(i); arrayList.add(i); } long time = System.nanoTime(); for(int i = 0; i < MAX_VAL; i++) { linkedList.add(MAX_VAL/2, i); } System.out.println("LL time: " + (System.nanoTime() - time)); time = System.nanoTime(); for(int i = 0; i < MAX_VAL; i++) { arrayList.add(MAX_VAL/2, i); } System.out.println("AL time: " + (System.nanoTime() - time)); }

從中間插入結果:

怎么會這樣, 不應該是LinkedList更快嗎? ArrayList底層是數組, 添加數據需要移動后面的數據, 而LinkedList使用的是鏈表, 直接移動指針就行, 按理說應該是LinkedList更快.

再來看

從尾插入

public static void main(String[] args){
        final int MAX_VAL = 10000;
        List<Integer> linkedList = new LinkedList<Integer>();
        List<Integer> arrayList = new ArrayList<Integer>();
        for(int i = 0; i < MAX_VAL; i++) {
            linkedList.add(i);
            arrayList.add(i);
        }
        long time = System.nanoTime();
        for(int i = 0; i < MAX_VAL; i++) {
            linkedList.add(i);
        }
        System.out.println("LL time: " + (System.nanoTime() - time));
        time = System.nanoTime();
        for(int i = 0; i < MAX_VAL; i++) {
            arrayList.add(i);
        }
        System.out.println("AL time: " + (System.nanoTime() - time));
    }

從頭開始插入

public static void main(String[] args){
        final int MAX_VAL = 10000;
        List<Integer> linkedList = new LinkedList<Integer>();
        List<Integer> arrayList = new ArrayList<Integer>();
        for(int i = 0; i < MAX_VAL; i++) {
            linkedList.add(i);
            arrayList.add(i);
        }
        long time = System.nanoTime();
        for(int i = 0; i < MAX_VAL; i++) {
            linkedList.add(0,i);
        }
        System.out.println("LL time: " + (System.nanoTime() - time));
        time = System.nanoTime();
        for(int i = 0; i < MAX_VAL; i++) {
            arrayList.add(0,i);
        }
        System.out.println("AL time: " + (System.nanoTime() - time));
    }

結果

然后從三分之一的位置開始插入

結果

從三分之二的位置插入

結果

 

源碼部分

LinkedList源碼

// 在index前添加節點,且節點的值為element
public void add(int index, E element) {
    addBefore(element, (index==size ? header : entry(index)));
}

// 獲取雙向鏈表中指定位置的節點
private Entry<E> entry(int index) {
    if (index < 0 || index >= size)
        throw new IndexOutOfBoundsException("Index: "+index+
                                            ", Size: "+size);
    Entry<E> e = header;
    // 獲取index處的節點。
    // 若index < 雙向鏈表長度的1/2,則從前向后查找;
    // 否則,從后向前查找。
    if (index < (size >> 1)) {
        for (int i = 0; i <= index; i++)
            e = e.next;
    } else {
        for (int i = size; i > index; i--)
            e = e.previous;
    }
    return e;
}

// 將節點(節點數據是e)添加到entry節點之前。
private Entry<E> addBefore(E e, Entry<E> entry) {
    // 新建節點newEntry,將newEntry插入到節點e之前;並且設置newEntry的數據是e
    Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
    // 插入newEntry到鏈表中
    newEntry.previous.next = newEntry;
    newEntry.next.previous = newEntry;
    size++;
    modCount++;
    return newEntry;

從中,我們可以看出:通過add(int index, E element)向LinkedList插入元素時。先是在雙向鏈表中找到要插入節點的位置index;找到之后,再插入一個新節點
雙向鏈表查找index位置的節點時,有一個加速動作若index < 雙向鏈表長度的1/2,則從前向后查找; 否則,從后向前查找

接着,我們看看ArrayList.java中向指定位置插入元素的代碼。如下:

// 將e添加到ArrayList的指定位置
public void add(int index, E element) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(
        "Index: "+index+", Size: "+size);

    ensureCapacity(size+1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
         size - index);
    elementData[index] = element;
    size++;
}

ensureCapacity(size+1) 的作用是“確認ArrayList的容量,若容量不夠,則增加容量。
真正耗時的操作是 System.arraycopy(elementData, index, elementData, index + 1, size - index);

 

實際上,我們只需要了解: System.arraycopy(elementData, index, elementData, index + 1, size - index); 會移動index之后所有元素即可這就意味着,ArrayList的add(int index, E element)函數,會引起index之后所有元素的改變!

 

結論

現在大概知道了,插入位置的選取對LinkedList有很大的影響,一直往數據中間部分插入刪除的時候,ArrayList比LinkedList更快

原因大概就是當數據量大的時候,system.arraycopy的效率要比每次插入LinkedList都需要從端查找index和分配節點node來的更快。

總之,對於99%或更多的現實情況,ArrayList是更好的選擇,並且利用LinkedList的狹隘優勢需要非常小心。

參考:https://stackoverflow.com/questions/16808777/is-linkedlist-really-faster-than-arraylist-in-the-case-of-insertion-in-the-middl

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM