Java專欄:以動畫的方式帶你深入理解Java源碼ArrayList!!!
大家好,我是老黑[1]。
今天我們來研究研究Java集合ArrayList的源碼,廢話不多說,開搞。
先拋出幾個問題:
你是否知道ArrayList的存儲原理? 你是否知道ArrayList的擴容原理? 你知道ArrayList什么時候擴容嗎? 你知道ArrayList是線程安全的嗎? 你知道ArrayList與LinkedList的區(qū)別嗎?
(答案與ArrayList生命周期動畫放在文末~)
再預(yù)覽一下本文大綱:

ArrayList概述
ArrayList是Java語言中最常見的集合工具類之一,它繼承AbstractList,實現(xiàn)了以下接口:
List(列表)
RandomAccess(順序訪問)
Cloneable(可克?。?/p>
Serializable(可序列化)
從它的繼承與實現(xiàn)來看,ArrayList是一個可克隆,可序列化,可順序訪問的列表。
ArrayList的存儲原理
在ArrayList中,內(nèi)部擁有4個屬性:

從上圖發(fā)現(xiàn),ArrayList的實現(xiàn)原理內(nèi)部其實是一個對象數(shù)組:Object[],然后還有一個屬性size用來表示鏈表的實際長度。

源碼分析
增
add(E object)


add(int index, E object)


請問:這個方法是線程安全的嗎?
我們再看一個動畫演示:

所以,add(int index, E object)不是線程安全的。
刪
remove(int index)

remove(Object o)

改
set(int index, E object)

修改與刪除不同,修改沒有類似CAS(比較并且交換)的API。
在這里多提一嘴,我們要知道,一個列表存放的到底是什么?
沒錯,存放的是對象的地址,而不是真正的對象,下面再來看一下動畫演示如何修改的:

查
查詢比較簡單,直接根據(jù)下標獲取數(shù)據(jù)即可。

迭代器
ArrayList.Itr迭代器

迭代器的這些方法其實都是我們經(jīng)常會使用到的,hasNext、next、remove等等。


小結(jié):
使用迭代器后,在迭代器外部最好是不要操作列表,否則在后續(xù)使用這個迭代器時很有可能將拋出異常; 使用next之前一般都會使用hasNext進行判斷; 使用remove之前必須使用next方法; 使用remove方法后迭代器內(nèi)部的expectedModCount會同步更新,所以在迭代器內(nèi)部remove是允許的; remove方法不允許連續(xù)使用兩次,因為在第一次使用后lastRet就會置為-1,下次remove時判斷l(xiāng)astRet<0則拋出IllegalStateException。
ArrayList.ListItr迭代器
ListItr是繼承了Itr的,所以Itr的所有功能在ListItr都有。
ListItr與Itr的區(qū)別是:
Itr只能從前往后迭代,并且是不允許查找上一個數(shù)據(jù);
而ListItr則允許從前往后和從后往前兩種迭代方向,同時也允許查找上一個數(shù)據(jù),并且也允許從中間某一個位置開始遍歷。
遍歷方式
準備
Listfor循環(huán)
for?(int?i?=?0;?i?????Object?obj?=?list.get(i);
????System.out.println(obj);
?
????//?remove
????//?不推薦,原因:內(nèi)部會進行數(shù)組塊移動,性能低
????//?list.remove(obj);
}
增強for循環(huán):foreach
//?foreach遍歷的本質(zhì)是迭代器遍歷
for?(Object?obj?:?list)?{
????System.out.println(obj);
?
????//?remove
????//?反對,原因:如果在里面remove元素的話,會拋ConcurrentModificationException異常
????//?list.remove(obj);
}
老黑在這里額外提一嘴,foreach遍歷的本質(zhì)是迭代器遍歷,所以,在迭代器遍歷的時候,使用list.remove方法,會修改list內(nèi)部的modCount,而迭代器內(nèi)部的expectedModCount并沒有改變,所以在下一次迭代的時候,迭代器內(nèi)部檢查將會拋出ConcurrentModificationException異常。
迭代器遍歷(推薦)
Iterator?iterator?=?list.iterator();
while?(iterator.hasNext())?{
????Object?obj?=?iterator.next();
????System.out.println(obj);
?
????//?remove
????//?推薦,不管是遍歷還是remove都推薦
????iterator.remove();
}
lambda表達式遍歷
list.forEach(item?->?{
????System.out.println(item);
?
????//?遍歷的話,推薦
????//?remove的話,不推薦,remove會拋
????//?ConcurrentModificationException異常
});
Stream遍歷
list.stream().forEach(item?->?{
????System.out.println(item);
?
????//?遍歷的話,推薦
????// remove的話,不管是串行流還是并行流都不推薦,remove會拋ConcurrentModificationException異常。
});
對于Stream,后面我們單獨出一篇文章來了解它的原理。
對于線程安全問題,我們的解決方案可以有:
使用api之前獲取鎖,如synchronized、Lock等;
使用Collections.synchronizedList();
List的生命周期

今天我們講解暫且告一段落了,感謝大家的閱讀。
下一篇我們就來研究LinkedList的原理實現(xiàn)。
-?
?| 更多精彩文章 -
▽加我微信,交個朋友 長按/掃碼添加↑↑↑




