ArrayList部分原始碼解析(JDK8)
List介面繼承自Collction(單列集合)介面,而ArrayList是List介面的一個重要實現類,當學習了ArrayList後再看其他實現類,如Vector和LinkedList(前者可以看作ArrayList的執行緒安全版,後者是ArrayList的連結串列版)
本文選取ArrayList類部分原始碼進行解析
目錄
底層資料結構
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{ ... transient Object[] elementData; // non-private to simplify nested class access ... }
ArrayList的底層資料結構是一個物件陣列
當ArrayList物件呼叫add方法新增集合元素時,集合元素就被新增到這個名為elementData的Object陣列中
新增方法---add
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; }
add方法是兩個互為過載方法
前者是普通的新增元素
private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } 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); }
方法體首先呼叫方法ensureCapacityInternal(size + 1),若elementData從未新增過元素,則獲取size+1與private static final int DEFAULT_CAPACITY = 10; 比較,較大者則為初始長度,後再比較當前陣列長度,若此時長度比陣列長度大且不超過Integer最大值2147483647,則通過grow方法獲取一個原陣列長度1.5倍的新elementData陣列
後者是指定索引位置新增元素,只是在前者的基礎上多了索引是否在elementData陣列長度範圍內的檢查 若超出則丟擲陣列越界異常IndexOutOfBoundsException
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
在陣列長度範圍內則呼叫System.arraycopy方法獲得一個在指定索引處添加了新元素的新陣列
檢查包含方法---contains
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
通過檢視原始碼我們可以知道,contains方法底層依賴於equals()方法,這意味著當我們的ArrayList物件儲存的是對自定義物件時,需要重寫equals方法,否則預設的equals方法比較的是地址,沒什麼意義
與Vector、LinkedList部分程式碼對比
學習了ArrayList的重點方法後,我們再來對比Vector與LinkedList方法,便能發現 其實原理都差不多,以add方法為例
Vector
public synchronized boolean add(E e) { // 執行緒安全
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
在方法處加了synchronized關鍵字 保證了執行緒安全
LinkedList
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
Node<E>相當於雙向連結串列的一個節點,節點儲存著上一節點位置及下一節點位置,通過transient Node<E> first;指向雙向連結串列頭結點