java中List介面的實現類 ArrayList,LinkedList,Vector 的區別 list實現類原始碼分析
java面試中經常被問到list常用的類以及內部實現機制,平時開發也經常用到list集合類,因此做一個原始碼級別的分析和比較之間的差異。
首先看一下List介面的的繼承關係:
list介面繼承Collection介面,Collection介面繼承Iterable介面。
Iterable介面定義的方法:
Collection介面中定義的方法:public interface Iterable<T> { /** * Returns an iterator over a set of elements of type T. * * @return an Iterator. */ Iterator<T> iterator(); }
所以實現list介面的子類必須實現Iterable和Collection介面中的方法。Iterable可以進行元素的迭代。package java.util; public interface Collection<E> extends Iterable<E> { int size(); boolean isEmpty(); boolean contains(Object o); Iterator<E> iterator(); Object[] toArray(); <T> T[] toArray(T[] a); boolean add(E e); boolean remove(Object o); boolean containsAll(Collection<?> c); boolean addAll(Collection<? extends E> c); boolean removeAll(Collection<?> c); boolean retainAll(Collection<?> c); void clear(); boolean equals(Object o); int hashCode(); }
List特性:
- 可以存放同一種類型的元素。
- 內部維護元素之間的順序,是有序集合。
- 元素是可以重複的。
在Java中List介面有3個常用的實現類,分別是ArrayList、LinkedList、Vector。
區別如下:
- ArrayList內部儲存的資料結構是陣列儲存。陣列的特點:元素可以快速訪問。每個元素之間是緊鄰的不能有間隔,缺點:陣列空間不夠元素儲存需要擴容的時候會開闢一個新的陣列把舊的陣列元素拷貝過去,比較消效能。從ArrayList中間位置插入和刪除元素,都需要迴圈移動元素的位置,因此陣列特性決定了陣列的特點:
- Vector內部實現和ArrayList一樣都是陣列儲存,最大的不同就是它支援執行緒的同步,所以訪問比ArrayList慢,但是資料安全,所以對元素的操作沒有併發操作的時候用ArrayList比較快。
- LinkedList內部儲存用的資料結構是連結串列。連結串列的特點:適合動態的插入和刪除。訪問遍歷比較慢。另外不支援get,remove,insertList方法。可以當做堆疊、佇列以及雙向佇列使用。LinkedList是執行緒不安全的。所以需要同步的時候需要自己手動同步,比較費事,可以使用提供的集合工具類例項化的時候同步:具體使用List<String> springokList=Collections.synchronizedCollection(new 需要同步的類)。
原始碼分析:
ArrayList類: public boolean add(E e) {
//確保陣列儲存空間是否已經滿了,慢了擴容
ensureCapacityInternal(size + 1); // Increments modCount!!
//計算儲存的位置
elementData[size++] = e;
return true;
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
Vector中:
private void ensureCapacityHelper(int minCapacity) {
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object[] oldData = elementData;
int newCapacity = (capacityIncrement > 0) ?
(oldCapacity + capacityIncrement) : (oldCapacity * 2);
if (newCapacity < minCapacity) {
newCapacity = minCapacity;
}
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
ArrayList和Vector主要區別如下:
(1)同步性:
Vector是執行緒安全的,也就是說是它的方法之間是執行緒同步的,而ArrayList是執行緒序不安全的,它的方法之間是執行緒不同步的。如果只有一個執行緒會訪問到集合,那最好是使用ArrayList,因為它不考慮執行緒安全,效率會高些;如果有多個執行緒會訪問到集合,那最好是使用Vector,因為不需要我們自己再去考慮和編寫執行緒安全的程式碼。
備註:對於Vector&ArrayList、Hashtable&HashMap,要記住執行緒安全的問題,記住Vector與Hashtable是舊的,是java一誕生就提供了的,它們是執行緒安全的,ArrayList與HashMap是java2時才提供的,它們是執行緒不安全的。
(2)資料增長:
ArrayList與Vector都有一個初始的容量大小,當儲存進它們裡面的元素的個數超過了容量時,就需要增加ArrayList與Vector的儲存空間,每次要增加儲存空間時,不是隻增加一個儲存單元,而是增加多個儲存單元,每次增加的儲存單元的個數在記憶體空間利用與程式效率之間要取得一定的平衡。Vector預設增長為原來兩倍,而ArrayList的增長策略在文件中沒有明確規定(從原始碼看到的是增長為原來的1.5倍)。ArrayList與Vector都可以設定初始的空間大小,Vector還可以設定增長的空間大小,而ArrayList沒有提供設定增長空間的方法。
總結:即Vector增長原來的一倍,ArrayList增加原來的0.5倍。空說無憑原始碼看看:
ArrayList中增長定義:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
Vector中增長定義:
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
ArrayList中什麼時候觸發克隆陣列:
- 構造引數是Collection集合子類可能觸發。
- trimToSize的時候觸發。
- grow的時候觸發。
- clone的時候觸發。
- toArray的時候觸發。