Java8 ArrayList源碼分析
java.util.ArrayList
是最常用的工具類之一, 它是一個線程不安全的動態數組. 本文將對JDK 1.8.0中ArrayList實現源碼進行簡要分析.
ArrayList
底層采用Object[]
來存儲, 每次添加元素前都會檢查數組是否有足夠空間容納新的元素.
若數組空間不足則會進行擴容操作, 即創建一個容量更大的數組 並將已有的元素復制到新數組中. 默認情況下新數組的容量是當前容量的1.5倍.
ArrayList使用Arrays.copyOf
和System.arraycopy
調用原生(native)方法進行數組復制, 以提高效率.
addAll
, removeAll
等方法中通常使用c.toArray
ArrayList提供了iterator()
和listIterator()
兩種叠代器, 前者只能向後移動, 而後者可以雙向移動.
iterator()
只能刪除上一個訪問的元素, 而listIterator()
還可以在遊標位置添加元素.
兩種叠代器都采用fail-fast機制, 即使用modCount
記錄結構性改變(添加刪除元素等)的次數, 叠代器在移動前會檢查modCount
是否發生改變. 若modCount
改變, 則拋出異常中止叠代. 該方法是為了防止其它線程修改容器造成叠代結果不一致.
數據結構與構造器
在介紹構造器之前, 首先介紹一下ArrayList
的數據結構:
// 默認初始容量
private static final int DEFAULT_CAPACITY = 10;
/**
* elementData是實際存儲數據的緩沖區
* 其類型為Object[], 即在內部用Object類存儲元素在取出時進行類型轉換
* 訪問控制為默認(包內訪問)是為了便於內部類訪問
* transient關鍵字表示不對該域進行序列化, ArrayList內部重寫了序列化/反序列化方法
*/
transient Object[] elementData;
// 當前元素數目
private int size;
// 用於表示空實例的數組
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 默認構造器使用的空數組
* 當elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA時, 首次添加元素會使elementData擴容到DEFAULT_CAPACITY
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
接下來可以閱讀ArrayList
的幾個構造器:
// 按照指定初始容量進行初始化
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 指定容量為0時使用EMPTY_ELEMENTDATA, 而非重新初始化空數組
this.elementData = EMPTY_ELEMENTDATA;
} else {
// 不允許負容量
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
}
}
/**
* 按照DEFAULT_CAPACITY進行初始化
* 構造時並未實際建立緩沖區, 在首次添加元素時才會擴容到DEFAULT_CAPACITY
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 根據其它集合對象創建
* 默認調用Collection.toArray方法,
* 若toArray方法返回類型不是Object[], 則利用Arrays.copyOf進行類型轉換
*/
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;
}
}
Arrays.copyOf
用於復制數組, 其封裝了原生(native)方法System.arraycopy
, 具有很高的效率.
ArrayList
中廣泛使用這兩個方法用於擴容, 插入等操作.
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
添加元素
ArrayList
的底層數據結構為數組, 每次向其中添加元素前都會檢查數組容量是否足夠. 若數組已滿則會進行擴容操作.
首先閱讀添加單個元素的方法add(E)
:
// 向數組末尾添加一個元素, 返回值代表數組是否改變
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
// 對於默認構造器創建的實例, 保證容量不小於DEFAULT_CAPACITY
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
// modCount記錄了實例發生結構性變化的次數, 用於叠代器的fail-fast機制
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// 計算擴容後新容量, 默認為原容量的1.5倍
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
// oldCapacity的1.5倍已經溢出, 所以出現反而變小的情況
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 若大於MAX_ARRAY_SIZE則由hugeCapacity取上限
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 創建新數組並把原有元素移動到新數組中
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
在制定位置添加元素的add(index, e)
方法非常類似:
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
// elementData類型一定為Object[], 不用Arrays.copyOf進行類型檢查直接調用System.arraycopy即可
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
addAll
方法調用c.toArray
獲取c中所有元素:
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
訪問元素
get
方法可以訪問指定位置的元素:
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E elementData(int index) {
return (E) elementData[index];
}
set
方法用於修改某位置的元素, 未發生結構性改變不會修改modCount
:
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
toArray
方法可以將ArrayList
中所有元素作為數組返回:
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a‘s runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
出於類型安全的原因, 建議使用第二個toArray
方法:
List<String> list = new ArrayList<>();
// add sth in list
String[] arr = new String[list.size()];
arr = list.toArray(arr);
刪除元素
remove(index)
方法用於移除指定位置的元素:
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;
}
使用System.arraycopy
將index後面的元素向前移動一位, 覆蓋被刪除的元素.
將最後位置上的元素設為null便於GC進行回收.
remove(obj)
方法會移除第一個與obj相同的元素, 相同關系使用obj.equals
方法來判斷:
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
}
removeAll(c)
方法移除所有包含在容器c中的元素, retainAll(c)
方法移除所有未包含在容器c中的元素.
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
實際完成該操作的是batchRemove
方法:
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
// 遍歷ArrayList, 使用`c.contains`判斷是否包含
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
// 將需要保留的元素移動到數組前部
elementData[w++] = elementData[r];
} finally {
// 保持與AbstractCollection的行為一致
// 即使c.contains拋出異常仍完成操作
if (r != size) {
// r != size 說明發生了contains異常.
// 將後部未判斷的部分移動到前面予以保留
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// w != size 說明有元素被刪除, 執行清理
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
叠代器
ArrayList提供了兩個叠代器: iterator()
和listIterator()
. 它們都采用fail-fast機制, 即當叠代器遍歷過程中容器發生結構性改變時, next方法會拋出ConcurrentModificationException
異常, 終止叠代.
所謂結構性改變是指modCount
發生改變的情況, 所有的add, removey操作以及clear()
方法都會修改modCount
. fail-fast機制主要為了應對其它線程修改容器導致的不一致問題.
首先閱讀iterator()
源碼:
// 獲得叠代器實例
public Iterator<E> iterator() {
return new Itr();
}
// 叠代器內部實現類
private class Itr implements Iterator<E> {
int cursor; // 下一個要返回元素的下標
int lastRet = -1; // 上一個返回元素的下標, 默認為-1.
int expectedModCount = modCount;
// 檢查是否可以繼續遍歷
public boolean hasNext() {
return cursor != size;
}
// 返回cursor指向的元素, 並將cursor後移一個位置
@SuppressWarnings("unchecked")
public E next() {
// 檢查modCount是否一致
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
// 刪除上一次返回的元素
public void remove() {
// 檢查是否返回過元素(成功調用過next方法), 且該元素未被刪除
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
// 刪除元素
ArrayList.this.remove(lastRet);
// 修正遊標位置
cursor = lastRet;
// 標記上次返回的元素已被刪除, 避免誤刪
lastRet = -1;
// 更新expectedModCount, 保證叠代器可以繼續執行
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
listIterator()
可以雙向移動, 除了刪除元素外還可以在遊標位置添加元素:
public ListIterator<E> listIterator() {
return new ListItr(0);
}
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
super();
cursor = index;
}
public boolean hasPrevious() {
return cursor != 0;
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor - 1;
}
@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i];
}
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
ArrayList
還有兩個內部類用於處理子序列操作:
SubList extends AbstractList<E>
ArrayListSpliterator<E> implements Spliterator<E>
序列化
ArrayList的序列化會寫入modCount
, size
和實際的元素. 同樣會檢查modCount
是否一致, 以避免並發問題.
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
自定義序列化機制的根本目的在於避免寫入無意義的字段. readObject
也按照同樣的策略進行重寫:
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
Java8 ArrayList源碼分析