ArrayList,LinkedList,Vector,Stack之間的區別

一,線程安全性

Vector、Stack:線程安全

ArrayList、LinkedList:非線程安全

 

二,實現方式

LinkedList:雙向鏈表

ArrayList,Vector,Stack:數組

 

三,容量擴展方面

由於ArrayList和Vector(Stack繼承自Vector,只在Vector的基礎上添加了幾個Stack相關的方法,故之後不再對Stack做特別的說明)使用數組實現,當數組長度不夠時,其內部會創建一個更大的數組,然後將原數組中的數據拷貝至新數組中

[java] view plain copy
  1. //ArrayList  
  2. public boolean add(E e) {  
  3.     ensureCapacity(size + 1);  
  4.     elementData[size++] = e;  
  5.     return true;  
  6. }  
  7.   
  8. public void ensureCapacity(int minCapacity) {  
  9.     modCount++;  
  10.     int oldCapacity = elementData.length;  
  11.     if (minCapacity > oldCapacity) {  
  12.         Object oldData[] = elementData;  
  13.         int newCapacity = (oldCapacity * 3)/2 + 1;  
  14.         //如果這次擴展不能滿足要求,那就直接用minCapacity  
  15.         if (newCapacity < minCapacity)  
  16.             newCapacity = minCapacity;  
  17.         // minCapacity is usually close to size, so this is a win:  
  18.         elementData = Arrays.copyOf(elementData, newCapacity);  
  19.     }  
  20. }  

如需擴展,則每次至少擴展至(原長度*3)/2 + 1

[java] view plain copy
  1. //Vector  
  2. public synchronized void addElement(E obj) {  
  3.     modCount++;  
  4.     ensureCapacityHelper(elementCount + 1);  
  5.     elementData[elementCount++] = obj;  
  6. }  
  7.   
  8. private void ensureCapacityHelper(int minCapacity) {  
  9.     int oldCapacity = elementData.length;  
  10.     if (minCapacity > oldCapacity) {  
  11.         Object[] oldData = elementData;  
  12.         int newCapacity = (capacityIncrement > 0) ?  
  13.             (oldCapacity + capacityIncrement) : (oldCapacity * 2);  
  14.         //如果這次擴展不能滿足要求,那就直接用minCapacity  
  15.         if (newCapacity < minCapacity) {  
  16.             newCapacity = minCapacity;  
  17.         }  
  18.         elementData = Arrays.copyOf(elementData, newCapacity);  
  19.     }  
  20. }  

如果在創建Vector時不指定capacityIncrement(自動擴展長度)的值,如需擴展,則每次至少擴展至原長度的2倍

 

四,效率方面

這裏僅僅比較ArrayList和LinkedList之間的效率差異

1,查詢

ArrayList直接通過下標進行定位

[java] view plain copy
  1. //ArrayList  
  2. public E get(int index) {  
  3.     RangeCheck(index);//檢查下標是否超過數組長度  
  4.     return (E) elementData[index];  
  5. }  

LinkedList則需要進行遍歷,平均遍歷次數應爲n/4

[java] view plain copy
  1. //LinkedList  
  2. public E get(int index) {  
  3.     return entry(index).element;  
  4. }  
  5.       
  6. private Entry<E> entry(int index) {  
  7.     if (index < 0 || index >= size)  
  8.         throw new IndexOutOfBoundsException("Index: "+index+  
  9.                 ", Size: "+size);  
  10.     Entry<E> e = header;  
  11.     //size >>1 相當於size/2  
  12.     //由於LinkedList由雙向鏈表實現,故從離得較近的一端開始遍歷更快  
  13.     if (index < (size >> 1)) {  
  14.         for (int i = 0; i <= index; i++)  
  15.             e = e.next;  
  16.     } else {  
  17.         for (int i = size; i > index; i--)  
  18.             e = e.previous;  
  19.     }  
  20.     return e;  
  21. }  

對於指定位置查詢,由於可以通過下標直接進行定位,ArrayList的速度遠快於LinkedList

但是如果都爲首尾位置的查詢,情況會大爲不同,因爲LinkedList也是可以直接定位到首尾位置的

[java] view plain copy
  1. //LinkedList  
  2. public E getFirst() {  
  3.     if (size==0)  
  4.         throw new NoSuchElementException();  
  5.     return header.next.element;  
  6. }  
  7.   
  8. public E getLast()  {  
  9.     if (size==0)  
  10.         throw new NoSuchElementException();  
  11.     return header.previous.element;  
  12. }  

此時ArrayList和LinkedList的效率相同

2,插入

對於ArrayList,指定位置插入有可能首先需要對數組容量進行擴展,之後還有可能導致數組中的數據需要順次移動(代碼中通過數組拷貝實現,避免了數據一個一個的移動),極端情況下插入一個數據將進行兩次數組拷貝

[java] view plain copy
  1. //ArrayList  
  2. public void add(int index, E element) {  
  3.     if (index > size || index < 0)  
  4.         throw new IndexOutOfBoundsException(  
  5.                 "Index: "+index+", Size: "+size);  
  6.     ensureCapacity(size+1);  //如必要,將對數組容量進行擴展  
  7.     System.arraycopy(elementData, index, elementData, index + 1,  
  8.             size - index);  
  9.     elementData[index] = element;  
  10.     size++;  
  11. }  

如果不指定插入位置,則插入至數組末端,此時只需考慮可能的數組容量擴展對性能帶來的影響

[java] view plain copy
  1. //ArrayList  
  2. public boolean add(E e) {  
  3.     ensureCapacity(size + 1);   
  4.     elementData[size++] = e;  
  5.     return true;  
  6. }  

由於LinkedList是由鏈表實現的,並沒有指定位置插入的方法,即便如此,一切也顯得如此美好

[java] view plain copy
  1. //LinkedList  
  2. public boolean add(E e) {  
  3.     addBefore(e, header);  
  4.         return true;  
  5. }  
  6.       
  7. private Entry<E> addBefore(E e, Entry<E> entry) {  
  8.     Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);  
  9.     newEntry.previous.next = newEntry;  
  10.     newEntry.next.previous = newEntry;  
  11.     size++;  
  12.     modCount++;  
  13.     return newEntry;  
  14. }  

當然了,LinkedList可以直接將數據插入至首尾

[java] view plain copy
  1. //LinkedList  
  2. public void addFirst(E e) {  
  3.     addBefore(e, header.next);  
  4. }  
  5.   
  6. public void addLast(E e) {  
  7.     addBefore(e, header);  
  8. }  

總體來說,LinkedList效率高於ArrayList,即使在末尾插入,ArrayList也需要考慮可能的容量擴展對性能帶來的影響

3,修改

和查詢屬於同一種情況

4,刪除

指定位置的刪除和插入屬於同一種情況

除了刪除指定位置數據,ArrayList和LinkedList都包含一個clear()方法用來清除所有數據

[java] view plain copy
  1. //ArrayList  
  2. public void clear() {  
  3.     modCount++;  
  4.   
  5.     // Let gc do its work  
  6.     for (int i = 0; i < size; i++)  
  7.         elementData[i] = null;  
  8.     size = 0;  
  9. }  
[java] view plain copy
  1. //LinkedList  
  2. public void clear() {  
  3.     Entry<E> e = header.next;  
  4.     while (e != header) {  
  5.         Entry<E> next = e.next;  
  6.         e.next = e.previous = null;  
  7.         e.element = null;  
  8.         e = next;  
  9.     }  
  10.     header.next = header.previous = header;  
  11.     size = 0;  
  12.     modCount++;  
  13. }  
由於都需要進行遍歷,故效率相同
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章