關於ArrayList和LinkedList這兩個集合類的性能,網上不少文章表示:ArrayList的插入性能要比LinkedList差。今天忽然想測試下,這個結論是否準確。
html
編寫了以下代碼:java
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。那這究竟是爲什麼?數組
咱們來看看具體的源碼:ide
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)函數spa
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節點,並插入到連接表,而後分別調整新節點的前一節點的向後引用和後一節點的向前引用。code
看了這兩段代碼前面的結論也就有了答案。
咱們再來看看爲何向指定位置插入時,性能差距如此之大?
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,每一個細節都應該本身進行驗證,不能人云亦云。