ArrayList 源碼淺析
前言
ArrayList作為我們開發(fā)中最常用的集合,作為極高頻次使用的類,我們不妨閱讀源碼一談究竟。
介紹
ArrayList繼承關系如下
AaaryList主要實現(xiàn)了List接口,同時標記為可以序列化Serializable、可復制CloneAble、支持隨機訪問RandomAccess。
幾個重要的成員變量
- /** * 默認容量 */ private static final int DEFAULT_CAPACITY = 10; /** * 用于空實例的共享空數(shù)組實例。 */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * 用于默認大小的空實例的共享空數(shù)組實例。我們將其與空元素數(shù)據(jù)區(qū)分開來,以了解添加第一個元素時要膨脹多少。 */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * 存儲ArrayList元素的數(shù)組緩沖區(qū)。ArrayList的容量是此數(shù)組緩沖區(qū)的長度。 */ transient Object[] elementData; // non-private to simplify nested class access /** * ArrayList的大?。ㄋ脑財?shù))。 */ private int size;
數(shù)據(jù)結構
ArrayList底層就是一個數(shù)組,數(shù)組會隨著數(shù)據(jù)的增長而擴容,數(shù)組的擴容就是建立一個新的容量大的數(shù)組,然后把舊數(shù)組上面的數(shù)據(jù)復制進新數(shù)組。關于擴容,后面會詳細講解。
因為是數(shù)組,所以支持隨機訪問,且有序。
常用方法
ArrayList()無參構造方法
- public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
初始化數(shù)組為一個空數(shù)組。與空元素數(shù)據(jù)區(qū)分開來,以了解添加第一個元素時要膨脹多少。
add(E e) 添加元素
將指定的元素追加到此列表的末尾
- public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
- private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
當添加元素,會先檢查是否超出容量,如果超出,則需要擴容。
當?shù)谝淮翁砑釉貢r,size為默認值0,會計算出一個最小容量minCapacity,如果是無參構造創(chuàng)建的,則會取默認的容量10,
Math.max(DEFAULT_CAPACITY, minCapacity),這里傳入的minCapacity為0,所以獲取更大的10。
如果計算出的最小容量大于原容量minCapacity - elementData.length > 0,則會進行擴容。
- 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); }
擴容算法是,擴為老容量的1.5倍,如果擴容后的容量仍然小于需要的最小容量minCapacity,則新的容量就取最小容量。
如果擴容后的大小超過最大容量,則會進行下面的操作
- private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
計算出擴容后的容量后,進行擴容,也就是,新建一個數(shù)組初始化為新容量,然后復制舊元素到新數(shù)組。elementData = Arrays.copyOf(elementData, newCapacity);
- public static <T> T[] copyOf(T[] original, int newLength) { return (T[]) copyOf(original, newLength, original.getClass()); }
- 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; }
為什么不能在forEach里面修改列表
ArrayList在新增、刪除元素都會執(zhí)行modCount++
modCount定義在ArrayList的父類AbstractList。
- /** * 此列表在結構上被修改的次數(shù)。結構修改是指那些改變列表大小的修改,或者以某種方式干擾列表,使得正在進行的迭代可能產生不正確的結果。 迭代器和列表迭代器方法返回的迭代器和列表迭代器實現(xiàn)使用此字段。如果此字段的值意外更改,迭代器(或列表迭代器)將拋出ConcurrentModificationException以響應下一個、刪除、上一個、設置或添加操作。這提供了快速失效行為,而不是在迭代過程中面對并發(fā)修改時的非確定性行為。 子類使用此字段是可選的。如果子類希望提供fail fast迭代器(和列表迭代器),那么它只需在add(int,E)和remove(int)方法(以及它重寫的任何其他方法,這些方法會導致列表的結構修改)中增加該字段。對add(int,E)或remove(int)的單個調用只能向該字段添加一個,否則迭代器(和列表迭代器)將拋出虛假的ConcurrentModificationException。如果實現(xiàn)不希望提供故障快速迭代器,則可以忽略此字段。 */ protected transient int modCount = 0;
然后我們來看下forEach的實現(xiàn)。
- @Override public void forEach(Consumer<? super E> action) { Objects.requireNonNull(action); final int expectedModCount = modCount; @SuppressWarnings("unchecked") final E[] elementData = (E[]) this.elementData; final int size = this.size; for (int i=0; modCount == expectedModCount && i < size; i++) { action.accept(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } }
在遍歷前,會暫存modCount值,每次循環(huán)都判斷下modCount是否有更改,若更改了,里面跳出循環(huán),隨后拋出異常。