JAVA集合類之ArrayList和LinkedList性能比較

關於ArrayList和LinkedList這兩個集合類的性能,網上很多文章表示:ArrayList的插入性能要比LinkedList差。今天突然想測試下,這個結論是否準確。

編寫了如下代碼:

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;


public class Demo {

	public static void main(String[] args) {
		
		int count = 1000000;	//循環次數
		System.out.println("循環 " + count + " 次,ArrayList LinkedList 尾部插入性能測試:");
		
		List<Integer> lst = new ArrayList<Integer>();
		List<Integer> link = new LinkedList<Integer>();
		
		new Demo().testInsert(lst, count);
		new Demo().testInsert(link, count);
		
		int index = 0;			//插入位置
		count = 90000;
		System.out.println("\n循環 " + count + " 次,ArrayList LinkedList 指定位置插入性能測試:");

		lst = new ArrayList<Integer>();
		link = new LinkedList<Integer>();
		new Demo().testInsertForIndex(lst, count, index);
		new Demo().testInsertForIndex(link, count, index);
		
	}
	
	/**
	 * 向默認位置插入元素
	 * @param count	循環次數
	 */
	public void testInsert(List<Integer> lst, int count){
		long bTime = System.currentTimeMillis();
		for(int i=0; i<count; i++){
			lst.add(1);
		}
		long eTime = System.currentTimeMillis();
		
		System.out.println(lst.getClass().getName() + " 共耗時:" + (eTime - bTime) + " ms");
	}
	
	/**
	 * 向指定位置插入元素
	 * @param count	循環次數
	 * @param index	插入位置
	 */
	public void testInsertForIndex(List<Integer> lst, int count, int index){
		long bTime = System.currentTimeMillis();
		for(int i=0; i<count; i++){
			lst.add(index,1);
		}
		long eTime = System.currentTimeMillis();
		
		System.out.println(lst.getClass().getName() + " 共耗時:" + (eTime - bTime) + " ms");
	}
}

執行結果如下:

循環 1000000 次,ArrayList LinkedList 尾部插入性能測試:
java.util.ArrayList 共耗時:56 ms
java.util.LinkedList 共耗時:192 ms

循環 90000 次,ArrayList LinkedList 指定位置插入性能測試:
java.util.ArrayList 共耗時:3885 ms
java.util.LinkedList 共耗時:5 ms


根據結果發現,在都使用add(E e)函數添加元素時,LinkedList的性能反而不如ArrayList。那這到底是爲何?

我們來看看具體的源碼:

ArrayList的add(E e)函數

public boolean add(E e) {
    ensureCapacity(size + 1);  //數組擴容
    elementData[size++] = e;
    return true;
}

第一行代碼:用於給ArrayList中的元素數組進行擴容,且先判斷是否需要擴容,如果需要則擴容算法爲:目前容量*3/2+1

第二行代碼:向元素數組的尾部追加新元素


LinkedList的add(E e)函數

public boolean add(E e) {
    addBefore(e, header);
     return true;
}

private Entry<E> addBefore(E e, Entry<E> entry) {
    Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
    newEntry.previous.next = newEntry;
    newEntry.next.previous = newEntry;
    size++;
    modCount++;
    return newEntry;
}

addBefore函數首先創建一個新的Entry節點,並插入到鏈接表,然後分別調整新節點的前一節點的向後引用和後一節點的向前引用。


看了這兩段代碼前面的結論也就有了答案。


我們再來看看爲什麼向指定位置插入時,性能差距如此之大?

ArrayList的add(int index, E element)函數

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++;
}

此處可以看到每插入一個元素都會使用System.arraycopy進行數組的複製,可想而知這效率會有多低。


LinkedList的add(int index, E element)函數

public void add(int index, E element) {
    addBefore(element, (index==size ? header : entry(index)));
}

此處,依然調用了addBefore函數,性能與add(E e)沒有差異。


總結:作爲coder,每個細節都應該自己進行驗證,不能人云亦云。

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