一,線程安全性
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++;
- }