JDK原始碼閱讀之ArrayList
ArrayList簡介
List 介面的大小可變陣列的實現。實現了所有可選列表操作,並允許包括 null 在內的所有元素。除了實現 List 介面外,此類還提供一些方法來操作內部用來儲存列表的陣列的大小。(此類大致上等同於 Vector 類,除了此類是不同步的。)
從這句話中我們可以看出ArrayList的兩個特點,1-大小可變,2-基於陣列實現
size、isEmpty、get、set、iterator 和 listIterator 操作都以固定時間執行。add 操作以分攤的固定時間 執行,也就是說,新增 n 個元素需要 O(n) 時間。其他所有操作都以線性時間執行(大體上講)。與用於 LinkedList 實現的常數因子相比,此實現的常數因子較低。
從這段話中我們讀出的意思是:add方法與其他方法不一樣,其實他也是這個類的核心方法。
每個 ArrayList 例項都有一個容量。該容量是指用來儲存列表元素的陣列的大小。它總是至少等於列表的大小。隨著向 ArrayList 中不斷新增元素,其容量也自動增長。並未指定增長策略的細節,因為這不只是新增元素會帶來分攤固定時間開銷那樣簡單。
從這裡我們看出:ArrayList中有一個重要的變數:容量,他會貫穿這個類的核心 另外注意一點是:ArrayList不是執行緒安全的,而ArrayList替代的集合類Vector就是一個執行緒安全的類
ArrayList類圖
ArrayList 實現了List介面,RandomAccess介面–代表著可以隨機訪問,實現了Cloneable介面,可以實現複製,這裡的複製不是淺複製是深拷貝。還實現了Serializable介面,實現序列化。繼承了AbstractList介面。
ArrayList的重要方法簡介
構造方法
public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
構造方法一共有三個,其中一個是預設的建構函式,這個函式會初始化一個空陣列,新增元素後會擴容到預設的容量大小10!第二個建構函式是指定容量,還是初始化一個空的陣列,但是新增元素的時候不會擴容到預設的容量大小。第三個建構函式的引數是一個集合,他會將集合中的元素以及大小記錄。(注意:入宮容量為0,或者集合為空,那麼處理方法是和第一個建構函式一致的)
精華方法
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++;
}
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);
}
擴容的步驟可以概括為:
- 檢驗容量是否足夠;minCapacity - elementData.length > 0,一旦需要的容量大於現有的容量,那麼立即擴容。
- 擴充的容量是當前容量的1.5倍
- 檢驗容量是否足夠滿足需要的容量 補充:這裡的擴量的1.5倍沒有使用乘法,而是使用左移運算 oldCapacity + (oldCapacity >> 1)。
重點方法
remove
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
從原始碼中我們看出arrayList的移除操作還是比較複雜的,首先是確定需要移除的元素的下標,然後將後面的元素一一的往前移動,將最後一個元素置為null,並且將大小改變。其實從底層的實現我們可以猜到刪除元素這一個過程是怎樣的。 另外他們都有一個共同點就是對modCount這個變數加1了。
clean
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
一個迴圈將所有的元素置為null
clone
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
實現的是深拷貝,不是淺複製
一些簡單的,不常用的就不復制貼上了,閱讀原始碼的時候會有不一樣的感受。
補充
ArrayList也自己實現了兩個迭代器和一個包裝外殼SubList。
ArrayList的閱讀感想
ArrayList和父類相比,最明顯的特點是:父類是不可改變的集合,而ArrayList是大小可變的集合。和Vector類相比,最明顯的特點是:Vector是執行緒安全的,ArrayList不是執行緒安全。所以ArrayList的核心就是怎麼實現大小可變,也就是擴容的方法。
說明
本文是本人撰寫,如果對於讀者有什麼啟發或者幫助,那是我的榮幸。如果對於本文中有什麼錯誤,或者意見,可以留言或者聯絡我:[email protected]