ArrayList源碼解析--深入理解常用的JAVA對象

JAVA開發有很多常用的數據類型,例如:ArrayList,Vector,HashMap,HashTable,StringBuffer,StringBuilder等。本文嘗試從以下三個方面分析ArrayList源碼。

問題:

【Question1】:ArrayList如何存儲數據?

【Question2】:都說ArrayList非線程安全,Vector是線程安全,究竟怎么回事?

【Question3】:從性能方面考慮,使用ArrayList需要注意什么?

【Answer1】:類名當中的array就已經說明,它是以數組的形式存儲數據的。那么,緊接著3個問題來了:

1.1 數組初始容量capacity為多大?如果太小,很快就裝滿了。如果過大,數組又沒被填滿,就會造成內存的浪費。

1.2 數組被填滿之后再往里加數據,數組怎么應對?

1.3 數據太多,多到超過Integer.MAX_VALUE。數組怎么應對?

answer1.1:兩個辦法,要么用戶自己指定capacity的大小;要么給capacity一個初始值。ArrayList的實現中:

辦法一:讓用戶自己指定初始容量。此辦法可以申請少于10個元素容量的數組。

辦法二:初始容量為0。第一次添加數據時,申請默認容量為10的數組空間。把數據放入數組。此策略優點是,實例化ArrayList又不用時,可以避免內存浪費。該策略稱為惰性初始化。缺點,無法申請少于10個元素容量的數組。

answer1.2:add數據過程:

先檢查數組是否還有剩余元素空間,再添加數據。如果有,直接數組末尾添加數據。如果沒有,就先把當前數組copy到一個長50%的空數組中,再往新數組的尾部添加元素。

[舉例]

ArrayList添加元素過程與用整理箱裝東西類似。在沒有東西要裝之前是沒有買整理箱的,在有了第一件需要裝起來的物品之后,買了一個只能裝10件物品的小整理箱,然后把第一件物品放進去。再有新物品以來時,先檢查整理箱是還有空間,如果有就往里面放;如果沒有就買一個比現在整理箱大50%的新整理箱,然后舊整理箱里的物品全部轉移動新整理箱中,然后把新物品放入新整理箱。如此持續下去,當發現需要裝的物品太多,多到整個房子都裝不下了,就拋異常。

answer1.3:直接拋異常。

【Answer2】:Vector在add,get方法前加了"synchronized",而ArrayList沒有。

【Answer3】:實例化ArrayList時,指定ArrayList的容量。

copy數組的過程很費時間。所以,為了提高性能,盡可能事先估計列表長度,在實例化ArrayList時就指定長度。以避免添加元素過程中,反復通過copy數組來擴容。

[實驗驗證]:

1、實驗過程:

往ArrayList添加300w個字符串(隨機生成的int),分別執行10次,經過初始化長度的ArrayList平均用時50.1ms;未初始化長度的ArrayList平均用時148.1ms,每次添加300w個字符串,ArrayList擴容33次,也就是copy數組33次。

2、實驗結論:

2.1、實例化ArrayList時指定ArrayList的容量,確實可以提高性能;

2.2、性能相差3倍;

3、結論分析:

3.1、性能相差3倍,但性能差別的絕對值只有100ms左右,相差不是很大。原因很可能是實驗用的字符串對象比較小,所以copy數組的速度很快。如果是大型的POJO對象,差別會更明顯。

3.2、300w條字符串add操作在200ms之內完成。所以,如果要添加的對象不多,對象也不大,可以不用太關心性能的問題。

3.3、如果是大對象或者對象又比較多,最好考慮指定初始容量大小。當然,也可以在大量添加對象之前,調用

ensureCapacity方法預先擴容。

【后續任務】

數組擴容的其他算法。在ArrayList中采用了數組copy的辦法來擴容,可以考慮別的擴容算法。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容