ArrayList源码浅析
簡介:?`ArrayList`作為我們開發中最常用的集合,作為極高頻次使用的類,我們不妨閱讀源碼一談究竟。
前言
ArrayList作為我們開發中最常用的集合,作為極高頻次使用的類,我們不妨閱讀源碼一談究竟。
介紹
ArrayList繼承關系如下
AaaryList主要實現了List接口,同時標記為可以序列化Serializable、可復制CloneAble、支持隨機訪問RandomAccess。
幾個重要的成員變量
/*** 默認容量*/private static final int DEFAULT_CAPACITY = 10;/*** 用于空實例的共享空數組實例。*/private static final Object[] EMPTY_ELEMENTDATA = {};/*** 用于默認大小的空實例的共享空數組實例。我們將其與空元素數據區分開來,以了解添加第一個元素時要膨脹多少。*/private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};/*** 存儲ArrayList元素的數組緩沖區。ArrayList的容量是此數組緩沖區的長度。*/transient Object[] elementData; // non-private to simplify nested class access/*** ArrayList的大小(它包含的元素數)。*/private int size;數據結構
ArrayList底層就是一個數組,數組會隨著數據的增長而擴容,數組的擴容就是建立一個新的容量大的數組,然后把舊數組上面的數據復制進新數組。關于擴容,后面會詳細講解。
因為是數組,所以支持隨機訪問,且有序。
常用方法
ArrayList()無參構造方法
public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}初始化數組為一個空數組。與空元素數據區分開來,以了解添加第一個元素時要膨脹多少。
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 codeif (minCapacity - elementData.length > 0)grow(minCapacity);}當添加元素,會先檢查是否超出容量,如果超出,則需要擴容。
當第一次添加元素時,size為默認值0,會計算出一個最小容量minCapacity,如果是無參構造創建的,則會取默認的容量10,
Math.max(DEFAULT_CAPACITY, minCapacity),這里傳入的minCapacity為0,所以獲取更大的10。
如果計算出的最小容量大于原容量minCapacity - elementData.length > 0,則會進行擴容。
private void grow(int minCapacity) {// overflow-conscious codeint 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) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}計算出擴容后的容量后,進行擴容,也就是,新建一個數組初始化為新容量,然后復制舊元素到新數組。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在新增、刪除元素都會執行modCount++
modCount定義在ArrayList的父類AbstractList。
/*** 此列表在結構上被修改的次數。結構修改是指那些改變列表大小的修改,或者以某種方式干擾列表,使得正在進行的迭代可能產生不正確的結果。 迭代器和列表迭代器方法返回的迭代器和列表迭代器實現使用此字段。如果此字段的值意外更改,迭代器(或列表迭代器)將拋出ConcurrentModificationException以響應下一個、刪除、上一個、設置或添加操作。這提供了快速失效行為,而不是在迭代過程中面對并發修改時的非確定性行為。 子類使用此字段是可選的。如果子類希望提供fail fast迭代器(和列表迭代器),那么它只需在add(int,E)和remove(int)方法(以及它重寫的任何其他方法,這些方法會導致列表的結構修改)中增加該字段。對add(int,E)或remove(int)的單個調用只能向該字段添加一個,否則迭代器(和列表迭代器)將拋出虛假的ConcurrentModificationException。如果實現不希望提供故障快速迭代器,則可以忽略此字段。*/protected transient int modCount = 0;然后我們來看下forEach的實現。
@Overridepublic 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值,每次循環都判斷下modCount是否有更改,若更改了,里面跳出循環,隨后拋出異常。
原文鏈接
本文為阿里云原創內容,未經允許不得轉載。?
總結
以上是生活随笔為你收集整理的ArrayList源码浅析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阿里开源支持10万亿模型的自研分布式训练
- 下一篇: 如何将一棵LSM-Tree塞进NVM