ArrayList,LinkedList,Vector,Stack之間的區別
一,執行緒安全性
Vector、Stack:執行緒安全
ArrayList、LinkedList:非執行緒安全
二,實現方式
LinkedList:雙向連結串列
ArrayList,Vector,Stack:陣列
三,容量擴充套件方面
由於ArrayList和Vector(Stack繼承自Vector,只在Vector的基礎上添加了幾個Stack相關的方法,故之後不再對Stack做特別的說明)使用陣列實現,當陣列長度不夠時,其內部會建立一個更大的陣列,然後將原陣列中的資料拷貝至新陣列中
//ArrayList public boolean add(E e) { ensureCapacity(size + 1); elementData[size++] = e; return true; } public void ensureCapacity(int minCapacity) { modCount++; int oldCapacity = elementData.length; if (minCapacity > oldCapacity) { Object oldData[] = elementData; int newCapacity = (oldCapacity * 3)/2 + 1; //如果這次擴充套件不能滿足要求,那就直接用minCapacity if (newCapacity < minCapacity) newCapacity = minCapacity; // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); } }
如需擴充套件,則每次至少擴充套件至(原長度*3)/2 + 1
//Vector public synchronized void addElement(E obj) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = obj; } private void ensureCapacityHelper(int minCapacity) { int oldCapacity = elementData.length; if (minCapacity > oldCapacity) { Object[] oldData = elementData; int newCapacity = (capacityIncrement > 0) ? (oldCapacity + capacityIncrement) : (oldCapacity * 2); //如果這次擴充套件不能滿足要求,那就直接用minCapacity if (newCapacity < minCapacity) { newCapacity = minCapacity; } elementData = Arrays.copyOf(elementData, newCapacity); } }
如果在建立Vector時不指定capacityIncrement(自動擴充套件長度)的值,如需擴充套件,則每次至少擴充套件至原長度的2倍
四,效率方面
這裡僅僅比較ArrayList和LinkedList之間的效率差異
1,查詢
ArrayList直接通過下標進行定位
//ArrayList
public E get(int index) {
RangeCheck(index);//檢查下標是否超過陣列長度
return (E) elementData[index];
}
LinkedList則需要進行遍歷,平均遍歷次數應為n/4
//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; //size >>1 相當於size/2 //由於LinkedList由雙向連結串列實現,故從離得較近的一端開始遍歷更快 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; }
對於指定位置查詢,由於可以通過下標直接進行定位,ArrayList的速度遠快於LinkedList
但是如果都為首尾位置的查詢,情況會大為不同,因為LinkedList也是可以直接定位到首尾位置的
//LinkedList
public E getFirst() {
if (size==0)
throw new NoSuchElementException();
return header.next.element;
}
public E getLast() {
if (size==0)
throw new NoSuchElementException();
return header.previous.element;
}
此時ArrayList和LinkedList的效率相同
2,插入
對於ArrayList,指定位置插入有可能首先需要對陣列容量進行擴充套件,之後還有可能導致陣列中的資料需要順次移動(程式碼中通過陣列拷貝實現,避免了資料一個一個的移動),極端情況下插入一個數據將進行兩次陣列拷貝
//ArrayList
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
ensureCapacity(size+1); //如必要,將對陣列容量進行擴充套件
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
如果不指定插入位置,則插入至陣列末端,此時只需考慮可能的陣列容量擴充套件對效能帶來的影響
//ArrayList
public boolean add(E e) {
ensureCapacity(size + 1);
elementData[size++] = e;
return true;
}
由於LinkedList是由連結串列實現的,並沒有指定位置插入的方法,即便如此,一切也顯得如此美好
//LinkedList
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;
}
當然了,LinkedList可以直接將資料插入至首尾
//LinkedList
public void addFirst(E e) {
addBefore(e, header.next);
}
public void addLast(E e) {
addBefore(e, header);
}
總體來說,LinkedList效率高於ArrayList,即使在末尾插入,ArrayList也需要考慮可能的容量擴充套件對效能帶來的影響
3,修改
和查詢屬於同一種情況
4,刪除
指定位置的刪除和插入屬於同一種情況
除了刪除指定位置資料,ArrayList和LinkedList都包含一個clear()方法用來清除所有資料
//ArrayList
public void clear() {
modCount++;
// Let gc do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
//LinkedList
public void clear() {
Entry<E> e = header.next;
while (e != header) {
Entry<E> next = e.next;
e.next = e.previous = null;
e.element = null;
e = next;
}
header.next = header.previous = header;
size = 0;
modCount++;
}
由於都需要進行遍歷,故效率相同