最近翻看ArrayList的源碼,對ArrayList的繼承關系做了大概梳理,記錄如下!
繼承關系
為了更全面了解ArrayList,我們需要首先對ArrayList的繼承關系有個大概了解,ArrayList的UML圖譜如下:

下面,我們根據UML圖譜,自上而下逐個做個簡要介紹,便於ArrayList的理解!
Iterable
這是一個支持for-each
循環的接口,一共有三個方法:
1.iterator():可以獲得一個Iterator對象。Iterator我們都很熟悉了,它可以根據hasNext()
、next()
兩個方法進行循環迭代。
2.forEach():這是一個default方法,默認接收Consumer對象,通過for循環進行單對象操作。
3.spliterator():這也是一個default方法,可以得到一個Spliterator
對象,支持對象的並發操作。后面我們會對Spliterator
進行專門的講解!
Collection
它是集合操作的"root interface"
,每個Collection就代表不同的集合類型,例如:重復集合和非重復集合、有序集合和無序集合等。JDK中沒有對它進行直接的實現,只是提供了實現它的子接口,例如:Set
、List
等。
List
這是我們最熟悉的有序集合接口,它的實現類,ArrayList、LinkedList是我們最常用。它內部的方法也一目了然,不做過多介紹。

AbstractCollection
這個抽象類是對Collection接口的一個基本實現。
如果實現一個不可變集合,我們只需要繼承這個類,並實現它的iterator()
和size()
方法,當然,iterator()方法返回的iterator對象中的hasNext()
和next()
方法也需要實現。
如果實現一個可變集合,項目中就必須override它的add()
方法,默認該方法拋出異常UnsupportedOperationException
,同時,實現iterator()方法返回的iterator對象中的hasNext()
、next()
以及remove()
方法。
AbstractList
此類提供了List接口的基本實現,以最小化實現由“隨機訪問”數據存儲(例如數組)支持的該接口所需的工作量。
實現不可變List集合時,繼承該類,並實現其中的get()
、size()
方法。
實現可變List集合時,還需要實現其中的set()方法,如果集合是可變長度集合,override其中的add()
和remove()
方法。
重點屬性modCount
:
AbstractList中有個重要屬性
modCount
,int類型,一旦類內部結構被修改,該屬性就會進行累加,,用來表示該類被修改的次數。在AbstractList的子類中,會存在與modCount向對應的另一個屬性expectedModCount
,子類可以通過對比兩個值是否相等,來達到校驗該類是否被其他線程修改的目的,該功能稱為fast-fail
。例如:AbstractList中add()
方法都會對modCount
進行累加操作,如果一個線程A在對它進行遍歷add操作時,另一線程B也對它進行了add操作,那么A線程就會檢測到expectedModCount
與modCount
不一致,從而拋出異常ConcurrentModificationException
。
RandomAccess
這只是一個接口,內部沒有方法,它僅作為一個標記接口存在,表示List集合下的實現支持快速隨機訪問功能,簡單來說就是底層是數組實現的集合標識。
Serializable
序列化接口,至於接口為什么需要序列化,可以參考:對象序列化.
Cloneable
實現Cloneable接口的類,可以合法地使用Object的clone()
方法對該類實例進行按字段復制,沒有實現Cloneable接口的類調用Object的clone()
方法時,則會導致拋出異常CloneNotSupporteddException
。
這里涉及到淺克隆(shallow clone)和深克隆(deep clone)的知識,在此不做過多介紹!可參考:對象的深度復制和淺復制.
Itr
AbstractList與ArrayList內部都有一個內部類Itr,都實現了Iterator接口的hasNext()
與next()
方法,並將默認remove()
方法重寫,另外,ArrayList中還重寫了forEachRemaining()
方法,該方法是對未處理過的元素進行遍歷。
Itr對元素遍歷過程使用到了三個重要屬性:cursor、lastRet、expectedModCount:
cursor:表示下一個返回元素的指針;
lastRet:最后一次返回的元素指針,沒有的話,默認-1;
expectedModCount:初始化迭代器時,默認為AbstractList中的modCount
值,迭代過程中,會與modCount
進行比較,達到"fail-fast"
效果,保證線程同步安全,不相同時,就會拋出ConcurrentModificationException
異常。
ListItr
同理,AbstractList與ArrayList通過ListItr對迭代器Itr進行繼承,實現了迭代效果,兩則的不同點,ArrayList.ListItr比AbstractList.ListItr更加優化了。
另外,ListItr除了繼承Itr外,它還實現了ListIterator
。相比於父類Iterator
,ListIterator新增了如下方法:
hasPrevious():相對於
hasNext()
方法,判斷是否有前一個元素;
previous():相對於next()
方法,返回前一個元素;
nextIndex():下一個元素的index值;
previousIndex():前一個元素的index值;
remove():刪除當前元素;
set(E e):修改當前元素;
add(E e):新增元素;
總的來說,ListIterator使迭代器兼容向前、向后兩個方向的遍歷,並能夠對元素進行修改、添加和刪除。
SubList
這是一個支持對ArrayList局部操作的集合,從構造方法中可以看到,我們操作的是fromIndex
到toIndex
的parent
對象,offset
是迭代操作的偏移量。
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
因為它支持對集合從fromIndex
到toIndex
的區段進行隨機訪問,因此實現了RandomAccess接口,前面我們講過,這是一個隨機訪問標識。另外,我們對區段操作與ArrayList相同,所以繼承ArrayList。
ArrayListSpliterator
前面我們在Itrable中提到了spliterator()方法,它可以得到一個Spliterator
對象,支持並發操作。那么ArrayList中就對Spliterator
進行了具體實現,實現類就是ArrayListSpliterator
。
要想了解ArrayListSpliterator,我們首先來看一下它的三個字段屬性含義:
// 接收到的list對象
private final ArrayList<E> list;
// 開始位置
private int index; // current index, modified on advance/split
// 結束位置(不包含)
private int fence; // -1 until used; then one past last index
// 期望的ModCount值
private int expectedModCount; // initialized when fence set
其中,我們操作的元素區間就是:[index, fence),即:[index, fence-1]。
ArrayListSpliterator中有三個重要方法:
trySplit():通過"二分法"分隔List集合;
tryAdvance():消費其中單個元素,此時index會相應+1;
forEachRemaining():遍歷所有未消費的集合元素;
具體操作,我們舉例說明,首先我們ArrayListSpliterator新增一個toString()方法,便於打印觀察。
@Override
public String toString() {
return "[" + this.index + "," + getFence() + ")";
}
1.對trySplit()
舉例測試:
查看測試代碼
public static void main(String[] args) {
// 初始化集合
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i <= 10; i++) {
list.add(i);
}
// 創建ArrayListSpliterator對象
log.info("--------------創建ArrayListSpliterator對象-----------------");
ArrayListSpliterator<Integer> list_1 = new ArrayListSpliterator<>(list, 0, -1);
log.info("list_1:" + list_1); // [0,11)
// 對list_1進行分割
log.info("--------------對list_1進行分割-----------------");
ArrayListSpliterator<Integer> list_2 = list_1.trySplit();
log.info("list_1:" + list_1); // [5,11)
log.info("list_2:" + list_2); // [0,5)
// 分割流程:[0,11)(list_1) ---> [0,5)(list_2) + [5,11)(list_1)
// 對list_1和list_2進行分割
log.info("--------------對list_1和list_2進行分割-----------------");
ArrayListSpliterator<Integer> list_3 = list_1.trySplit();
ArrayListSpliterator<Integer> list_4 = list_2.trySplit();
log.info("list_1:" + list_1); // [8,11)
log.info("list_2:" + list_2); // [2,5)
log.info("list_3:" + list_3); // [5,8)
log.info("list_4:" + list_4); // [0,2)
// 分割流程:
// [0,5)(list_2) --> [0,2)(list_4) + [2,5)(list_2)
// [5,11)(list_1) --> [8,11)(list_1) + [5,8)(list_3)
// 測試集合地址
log.info("--------------測試集合地址-----------------");
log.info("(list_1.list == list_2.list) = " + (list_1.list == list_2.list));
log.info("(list_2.list == list_3.list) = " + (list_2.list == list_3.list));
log.info("(list_3.list == list_4.list) = " + (list_3.list == list_4.list));
}
打印結果:

從結果我們看到,因為Spliterator中都共享一個list,所以他們的list地址都相同,是同一個list對象。
2.對tryAdvance()
和forEachRemaining()
舉例測試:
查看測試代碼
public static void main(String[] args) {
// 初始化集合
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i <= 10; i++) {
list.add(i);
}
// tryAdvance操作
ArrayListSpliterator<Integer> list_1 = new ArrayListSpliterator<>(list, 0, -1);
list_1.tryAdvance(t -> log.info("tryAdvance:" + t + " "));
// forEachRemaining操作
list_1.forEachRemaining(t -> log.info("forEachRemaining:" + t + " "));
// 剩余元素
log.info("list_1:" + list_1);
log.info("left size:" + list_1.estimateSize());
}
執行結果:

從結果我們看到,tryAdvance()
只是對第一個元素(index=0)進行了操作,而forEachRemaining()
就從第二個元素(index=1)開始對未消費的元素進行遍歷輸出,它的index也隨着增加,最后遍歷完為11,剩余的size也變為0。
好了,關於ArrayList的繼承關系我們就介紹到這里,下面開始對ArrayList進行正式分析!
ArrayList分析
ArrayList的數據結構是以數組為基礎構建的,每個元素都存儲到了數組當中,ArrayList的size就是此數組的長度。這里我針對ArrayList中的主要功能點做個簡要介紹。
數組擴容:
數組初始化時默認數組為空對象DEFAULTCAPACITY_EMPTY_ELEMENTDATA,當添加第一個元素時,對數組進行擴容操作,默認擴容大小為10(DEFAULT_CAPACITY)。
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
// minCapacity = size + 1
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
擴容操作采用Arrays.copyOf(elementData, newCapacity)
,其底層是數組的copy:
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
ArrayList中對數據的擴容、縮容或者截取都是采用此方法操作。它是一個JVM提供的高效數組拷貝實現,至於它為什么高效,請參考這篇:System.arraycopy為什么快。
另外,在正常的擴容過程中,數組容積以1/2
的長度進行增長,一直到達Integer.MAX_VALUE - 8
。對於Integer.MAX_VALUE - 8
的解釋,JDK指出原因是一些VM會在數組中保留一些header words,導致超出內存空間而出現OutOfMemoryError。
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);
}
數組縮容:
數組縮容操作時(例如刪除),ArrayList中會找到需要移除的index,從index位置開始,將index+1后面的所有元素重新拷貝,同時,最后一位置為null,等待GC回收。
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;
}
數組遍歷:
我們通常使用到的數組遍歷都是采用的iterator()
方法獲取Iterator
對象,向后逐個遍歷,其實你也可以采用listIterator()
方法獲得一個ListIterator
對象實現向前遍歷。
public ListIterator<E> listIterator() {
return new ListItr(0);
}
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
"Fail-Fast"機制
ArrayList中通過modCoun
來實現"Fail-Fast"的錯誤檢測機制,當多個線程對同一集合的內容進行操作時,可能就會產生異常。
在ArrayList的迭代器初始化時,會賦值expectedModCount,在迭代過程中判斷modCount和expectedModCount是否一致。比如當A通過iterator去遍歷某集合的過程中,線程B修改了此集合,此時就會出現modCount和expectedModCount不一致,而導致拋出ConcurrentModificationException
異常。
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
集合區段操作
通過subList(int fromIndex, int toIndex)
方法我們就可以獲得指定索引區段的集合對象SubList
。這是一個偏移量控制的集合區段,可以理解為fromIndex-offset
變為0后的一個新集合,但要注意,任何對SubList
對象的修改操作,都將導致原集合對象修改,因為它們使用的是同一個地址。
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
集合分隔操作
當我們需要對集合對象進行多線程操作時,可以考慮將集合分隔,采用spliterator()
方法獲取到Spliterator
對象,利用其trySplit()
方法將其分隔開來,分別進行元素操作,分隔方式即為二分法
。
同樣,我們要注意,Spliterator
對象的任何修改,都將導致原集合元素的修改。
public Spliterator<E> spliterator() {
return new ArrayListSpliterator<>(this, 0, -1, 0);
}