從圖中能夠看出,ArrayList與LinkedList都是List接口的實現類,所以都實現了List的全部未實現的方法,只是實現的方式有所不一樣,(從中能夠看出面向接口的好處, 對於不一樣的需求就有不一樣的實現!),而List接口繼承了Collection接口,Collection接口又繼承了Iterable接口,所以能夠看出List同時擁有了Collection與Iterable接口的特性.java
ArrayList是List接口的可變數組的實現。實現了全部可選列表操做,並容許包括 null 在內的全部元素。除了實現 List 接口外,此類還提供一些方法來操做內部用來存儲列表的數組的大小。
每一個ArrayList實例都有一個容量,該容量是指用來存儲列表元素的數組的大小。它老是至少等於列表的大小。隨着向ArrayList中不斷添加元素,其容量也自動增加。自動增加會帶來數據向新數組的從新拷貝,所以,若是可預知數據量的多少,可在構造ArrayList時指定其容量。在添加大量元素前,應用程序也可使用ensureCapacity操做來增長ArrayList實例的容量,這能夠減小遞增式再分配的數量。
注意,此實現不是同步的。若是多個線程同時訪問一個ArrayList實例,而其中至少一個線程從結構上修改了列表,那麼它必須保持外部同步。這一般是經過同步那些用來封裝列表的對象來實現的。但若是沒有這樣的對象存在,則該列表須要運用{@link Collections#synchronizedList Collections.synchronizedList}來進行「包裝」,該方法最好是在建立列表對象時完成,爲了不對列表進行突發的非同步操做。數組
List list = Collections.synchronizedList(new ArrayList(...));複製代碼
建議在單線程中才使用ArrayList,而在多線程中能夠選擇Vector或者CopyOnWriteArrayList。bash
數組有個明顯的特色就是它的容量是固定不變的,一旦數組被建立則容量則沒法改變。因此在往數組中添加指定元素前,首先要考慮的就是其容量是否飽和。數據結構
若接下來的添加操做會時數組中的元素超過其容量,則必須對其進行擴容操做。受限於數組容量固定不變的特性,擴容的本質其實就是建立一個容量更大的新數組,再將舊數組的元素複製到新數組當中去。多線程
這裏以 ArrayList 的 添加操做爲例,來看下 ArrayList 內部數組擴容的過程。函數
public boolean add(E e) {
// 關鍵 -> 添加以前,校驗容量
ensureCapacityInternal(size + 1);
// 修改 size,並在數組末尾添加指定元素
elementData[size++] = e;
return true;
}複製代碼
能夠發現 ArrayList 在進行添加操做前,會檢驗內部數組容量並選擇性地進行數組擴容。在 ArrayList 中,經過私有方法 ensureCapacityInternal 來進行數組的擴容操做。下面來看具體的實現過程:post
// 內部數組的默認容量
private static final int DEFAULT_CAPACITY = 10;
// 空的內部數組
private static final Object[] EMPTY_ELEMENTDATA = {};
// 關鍵 -> minCapacity = seize+1,即表示執行完添加操做後,數組中的元素個數
private void ensureCapacityInternal(int minCapacity) {
// 判斷內部數組是否爲空
if (elementData == EMPTY_ELEMENTDATA) {
// 設置數組最小容量(>=10)
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}複製代碼
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 判斷結果爲 true,則表示接下來的添加操做會致使元素數量超出數組容量
if (minCapacity - elementData.length > 0){
// 真正的擴容操做
grow(minCapacity);
}
}複製代碼
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 關鍵-> 容量擴充公式
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 針對新容量的一系列判斷
if (newCapacity - minCapacity < 0){
newCapacity = minCapacity;
}
if (newCapacity - MAX_ARRAY_SIZE > 0){
newCapacity = hugeCapacity(minCapacity);
}
// 關鍵 -> 複製舊數組元素到新數組中去
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0){
throw new OutOfMemoryError();
}
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}複製代碼
關於 ArrayList 擴容操做,整個過程以下圖:gradle
如上圖所示,LinkedList底層使用的雙向鏈表結構,有一個頭結點和一個尾結點,雙向鏈表意味着咱們能夠從頭開始正向遍歷,或者是從尾開始逆向遍歷,而且能夠針對頭部和尾部進行相應的操做。this
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;spaNode(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
1.ArrayList是實現了基於動態數組的數據結構,LinkedList基於鏈表的數據結構。
2.對於隨機訪問get和set,ArrayList以爲優於LinkedList,由於LinkedList要移動指針。
3.對於新增和刪除操做add和remove,LinedList比較佔優點,由於ArrayList要移動數據。