java.util.ArrayList
是最經常使用的工具類之一, 它是一個線程不安全的動態數組. 本文將對JDK 1.8.0中ArrayList實現源碼進行簡要分析.java
ArrayList
底層採用Object[]
來存儲, 每次添加元素前都會檢查數組是否有足夠空間容納新的元素.數組
若數組空間不足則會進行擴容操做, 即建立一個容量更大的數組 並將已有的元素複製到新數組中. 默認狀況下新數組的容量是當前容量的1.5倍.安全
ArrayList使用Arrays.copyOf
和System.arraycopy
調用原生(native)方法進行數組複製, 以提升效率.數據結構
addAll
, removeAll
等方法中一般使用c.toArray
方法來獲取容器中全部元素.併發
ArrayList提供了iterator()
和listIterator()
兩種迭代器, 前者只能向後移動, 然後者能夠雙向移動.工具
iterator()
只能刪除上一個訪問的元素, 而listIterator()
還能夠在遊標位置添加元素.ui
兩種迭代器都採用fail-fast機制, 即便用modCount
記錄結構性改變(添加刪除元素等)的次數, 迭代器在移動前會檢查modCount
是否發生改變. 若modCount
改變, 則拋出異常停止迭代. 該方法是爲了防止其它線程修改容器形成迭代結果不一致.this
在介紹構造器以前, 首先介紹一下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
的幾個構造器:code
// 按照指定初始容量進行初始化 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(); } } }