先回顧一下List的框架圖
由圖中的繼承關係,能夠知道,ArrayList、LinkedList、Vector、Stack都是List的四個實現類。java
AbstractList是一個抽象類,它繼承於AbstractCollection。AbstractList實現List接口中除size()、get(int location)以外的函數。數組
AbstractSequentialList 是一個抽象類,它繼承於AbstractList。AbstractSequentialList 實現了「鏈表中,根據index索引值操做鏈表的所有函數」。安全
ArrayList 是一個數組隊列,至關於動態數組。它由數組實現,隨機訪問效率高,隨機插入、隨機刪除效率低。多線程
Stack 是棧,它繼承於Vector。它的特性是:先進後出(FILO, First In Last Out)。框架
在對ArrayList、LinkedList、Vector、Stack進行比較以前,咱們先來對他們進行一個性能測試,結合源碼和測試結果來對ArrayList、LinkedList、Vector、Stack進行詳細的分析。函數
import java.util.*; public class ListTest { private static final int COUNT = 100000; private static LinkedList linkedList = new LinkedList(); private static ArrayList arrayList = new ArrayList(); private static Vector vector = new Vector(); private static Stack stack = new Stack(); public static void main(String[] args) { // 換行符 System.out.println(); // 插入 insertByPosition(stack) ; insertByPosition(vector) ; insertByPosition(linkedList) ; insertByPosition(arrayList) ; // 換行符 System.out.println(); // 隨機讀取 readByPosition(stack); readByPosition(vector); readByPosition(linkedList); readByPosition(arrayList); // 換行符 System.out.println(); // 刪除 deleteByPosition(stack); deleteByPosition(vector); deleteByPosition(linkedList); deleteByPosition(arrayList); } // 獲取list的名稱 private static String getListName(List list) { if (list instanceof LinkedList) { return "LinkedList"; } else if (list instanceof ArrayList) { return "ArrayList"; } else if (list instanceof Stack) { return "Stack"; } else if (list instanceof Vector) { return "Vector"; } else { return "List"; } } // 向list的指定位置插入COUNT個元素,並統計時間 private static void insertByPosition(List list) { long startTime = System.currentTimeMillis(); // 向list的位置0插入COUNT個數 for (int i=0; i<COUNT; i++) list.add(0, i); long endTime = System.currentTimeMillis(); long interval = endTime - startTime; System.out.println(getListName(list) + " : insert "+COUNT+" elements into the 1st position use time:" + interval+" ms"); } // 從list的指定位置刪除COUNT個元素,並統計時間 private static void deleteByPosition(List list) { long startTime = System.currentTimeMillis(); // 刪除list第一個位置元素 for (int i=0; i<COUNT; i++) list.remove(0); long endTime = System.currentTimeMillis(); long interval = endTime - startTime; System.out.println(getListName(list) + " : delete "+COUNT+" elements from the 1st position use time:" + interval+" ms"); } // 根據position,不斷從list中讀取元素,並統計時間 private static void readByPosition(List list) { long startTime = System.currentTimeMillis(); // 讀取list元素 for (int i=0; i<COUNT; i++) list.get(i); long endTime = System.currentTimeMillis(); long interval = endTime - startTime; System.out.println(getListName(list) + " : read "+COUNT+" elements by position use time:" + interval+" ms"); } }
獲得的結果以下性能
Stack : insert 100000 elements into the 1st position use time:834 ms Vector : insert 100000 elements into the 1st position use time:818 ms LinkedList : insert 100000 elements into the 1st position use time:10 ms ArrayList : insert 100000 elements into the 1st position use time:822 ms Stack : read 100000 elements by position use time:5 ms Vector : read 100000 elements by position use time:3 ms LinkedList : read 100000 elements by position use time:6088 ms ArrayList : read 100000 elements by position use time:2 ms Stack : delete 100000 elements from the 1st position use time:857 ms Vector : delete 100000 elements from the 1st position use time:835 ms LinkedList : delete 100000 elements from the 1st position use time:6 ms ArrayList : delete 100000 elements from the 1st position use time:849 ms
根據結果,能夠很明顯的看出ArrayList、LinkedList、Vector、Stack的性能有很大的區別。測試
操做 | ArrayList | LinkedList | Vector | Stack |
---|---|---|---|---|
讀取 | 2ms | 6088ms | 3ms | 5ms |
插入 | 822ms | 10ms | 818ms | 834ms |
刪除 | 849ms | 6ms | 835ms | 857ms |
讀取:ArrayList > Vector > Stack > LinkedList
插入:LinkedList > Vector > ArrayList > Stack
刪除:LinkedList > Vector > ArrayList > Stack線程
// 在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,則從前向後查找; 不然,從後向前查找。指針
// 將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++; }
在這裏面有一個很是耗時的操做
System.arraycopy(elementData, index, elementData, index + 1, size - index);
該方法被標記了native,調用了系統的C/C++代碼,在JDK中是看不到的,但在openJDK中能夠看到其源碼。
該函數實際上最終調用了C語言的memmove()函數,所以它能夠保證同一個數組內元素的正確複製和移動,比通常的複製方法的實現效率要高不少,很適合用來批量處理數組。Java強烈推薦在複製大量數組元素時用該方法,以取得更高的效率。
public synchronized void insertElementAt(E obj, int index) { modCount++; if (index > elementCount) { throw new ArrayIndexOutOfBoundsException(index + " > " + elementCount); } ensureCapacityHelper(elementCount + 1); System.arraycopy(elementData, index, elementData, index + 1, elementCount - index); elementData[index] = obj; elementCount++; } public void add(int index, E element) { insertElementAt(element, index); }
能夠看到Vector和ArrayList是同樣的,都調用了System.arraycopy。因爲Stack和繼承與Vector,就不仔細分析了。
LinkedList隨機訪問的代碼 // 返回LinkedList指定位置的元素 public E get(int index) { return entry(index).element; } // 獲取雙向鏈表中指定位置的節點 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; }
從中,咱們能夠看出:經過get(int index)獲取LinkedList第index個元素時。先是在雙向鏈表中找到要index位置的元素;找到以後再返回。
雙向鏈表查找index位置的節點時,有一個加速動做:若index < 雙向鏈表長度的1/2,則從前向後查找; 不然,從後向前查找。
// 獲取index位置的元素值 public E get(int index) { RangeCheck(index); return (E) elementData[index]; } private void RangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException( "Index: "+index+", Size: "+size); }
咱們能夠看到ArrayList直接返回數組中index位置的元素,而不須要像LinkedList同樣進行查找。
經過源碼發現Vector和Stack的操做方式和ArrayList同樣,這裏就不詳細分析了。
private E remove(Entry<E> e) { if (e == header) throw new NoSuchElementException(); E result = e.element; e.previous.next = e.next; e.next.previous = e.previous; e.next = e.previous = null; e.element = null; size--; modCount++; return result; }
因爲刪除了某一節點所以調整相應節點的先後指針信息,以下:
e.previous.next = e.next;//預刪除節點的前一節點的後指針指向預刪除節點的後一個節點。 e.next.previous = e.previous;//預刪除節點的後一節點的前指針指向預刪除節點的前一個節點。
清空預刪除節點:
e.next = e.previous = null; e.element = null;
交給gc完成資源回收,刪除操做結束。
與ArrayList比較而言,LinkedList的刪除動做不須要「移動」不少數據,從而效率更高。
public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work return oldValue; }
恩,又是調用了System.arraycopy。
操做 | ArrayList | LinkedList | Vector | Stack |
---|---|---|---|---|
讀取 | O(1) | O(n) | O(1) | O(1) |
插入 | O(n) | O(1) | O(n) | O(n) |
刪除 | O(n) | O(1) | O(n) | O(n) |
因此,快速訪問ArrayList,快速增刪LinkedList,單線程均可以用,多線程只能用同步類Vector