1. 程式人生 > >java集合類原始碼詳解-ArrayList(2)

java集合類原始碼詳解-ArrayList(2)

上次關於ArrayList的結構沒有做總結。這次還是補充在自己部落格裡面吧。

ArrayList繼承自一個抽象類。實現了四個介面。

AbstractList繼承自AbstractCollection。AbstractCollection繼承自Object。

ArrayList底層結構是陣列。所以其特點就是,查詢(隨機訪問)快,增刪慢(因為每刪掉或者增加陣列中的一位或者多位,這個位置後面的數都會往前移),效率高。

那為什麼ArrayList要實現這個四個介面呢?

List:這個就不用說。

RandomAccess:實現這個介面只要是為了擁有隨機訪問功能。

Serializable:為了使其支援序列化。

Cloneable:為了覆蓋函式clone(),即支援克隆。

關於Cloneable。來自JDK1.8 google。的解釋:

//一個類實現Cloneable介面,以指示Object.clone()方法,該方法對於該類的例項進行現場複製是合法的。
//不實現Cloneable介面的例項上呼叫物件的克隆方法導致丟擲異常CloneNotSupportedException 。
//按照慣例,實現此介面的類應使用公共方法覆蓋Object.clone (受保護)。
// 有關覆蓋此方法的詳細資訊,請參閱Object.clone() 。
//注意,此介面不包含clone方法

這裡要注意,這裡雖然實現了Cloneable和RandomAccess這兩個介面,但是當你進入這兩個介面檢視的時候,會發現這兩個接口裡面什麼都沒有。

ArrayList裡面有很多的方法,這次測試三個,上次測試的無參構造和add()。這次測試指定容量的有參構造和add()和trimToSize()。

點選下一步,會跳轉到靜態變數那裡,這裡就略過,上篇部落格已經記錄了。

由於我們指定了預設引數,所以肯定會跳轉到有參構造裡面來。在執行ArrayList的無參構造時,會先執行ArrayList的父類

AbstractList的無參構造並且初始化modCount。

點選下一步,然後會回到ArrayList的有參構造繼續執行。此時會判斷使用者指定的初始容量是否是大於0

如果是大於0,那麼就new一個object型別的陣列。賦給elementData這個陣列。elementData上章部落格已經解釋了,這裡稍微再提一次。

elementData:transient Object[] elementData;
ArrayList基於陣列實現,用該陣列儲存資料, ArrayList 的容量就是該陣列的長度

如果這個初始化大小等於0,就會把EMPTY_ELEMENTDATA這個陣列賦給elementData。

如果上面的兩個條件都不滿足,就會丟擲異常-----非法引數異常

這裡程式肯定是走第一個if,因為我們指定的初始化容量是5,5顯然是大於0的。

此時有參構造執行結束,點選下一步,回到我們自己的程式碼,開始執行add。size是陣列的實際長度,這個時候還沒有新增任何元素,所以此時的size是0。

點選下一步執行ensureCapacityInternal()。這個函式就是對minCapacity進行確認。這裡可以看到minCapacity這個變數的值是1.這裡會進行一個邏輯判斷是否element==DEFAULT_EMPTY_ELEMENTDATA。其實就是看這個容器是不是預設初始化,也就是判斷是未指定初始容量。

這裡我們指定了初始容量,所以肯定是不會執行這個if,而是準備開始執行ensureExplicitInternal()這個函式。

點選下一步。此時最重要的一部就來了。判斷最小容量是否大於資料的當前容量。從下面的除錯資訊來看。

現在的最小容量是1.陣列當前容量我們指定了是5。我們現在的最小容量還沒有超出陣列的可容納範圍即陣列當前容量。

所以if判斷肯定是false即我們現在不需要對陣列進行擴容。

點選下一步,開始彈棧。開始把值新增進數組裡面。新增完後size+1。size的大小變成1。也就是現在的陣列的實際大小是1。

這裡為什麼呼叫ensureCapacityInternal()傳的值是size+1呢? 

因為add()方法,新增資料是一個一個的新增,所以當我們數組裡面沒有值,然後我們想新增值的時候,(此時size=0)是不是得至少保證陣列的最小容量是1。才能新增啊。依次類推,當陣列大小是5,7,6等等其他大小的時候,我們想新增資料,是不是都得保證此時的最小容量Capacity都得比之前至少要大1。

這裡開始測試trimToSize()方法。我們此時這個例子的陣列容量。elementData.length是5。size是1。可以說陣列容量(或者說陣列緩衝區)是遠大於陣列容納的數的數量。這會造成空間的浪費。再確定不再往容器裡面新增資料的時候才呼叫這個方法。

這裡我們再次點選下一步。這裡因為會對ArrayList物件的容量進行操作,所以會觸發modCount++。

再次點選下一步,此時會執行一個判斷。陣列實際的資料長度是否小於資料的容量。

這裡我們當然是True,從下面可以看出,一個是1,一個是5。

 再次點選下一步,開始執行一個三目運算。

elementData = (size == 0)
        ? EMPTY_ELEMENTDATA
        : Arrays.copyOf(elementData, size);

這句的執行是先判斷size是否為0,如果為0則返回True,將EMPTY_ELEMENTDATA(空陣列)賦給elementData。

這裡我們顯然是False。所以這裡會執行Arrays.copyOf(elementData, size)。將elementData這個陣列的長度變為size長度。

並且將新陣列返回給elementData。

 再點選下一步。

後續樓主會繼續學習集合類的原始碼,另外部落格什麼地方寫錯,請指出,大家一起學習進步!!