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

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

       最近在瘋狂的補基礎 在java中 最重要的知識之一 非集合類莫屬。這次在學習java集合類原始碼,採用的是傳統的方法,斷點除錯和寫測試程式碼。由於是剛開始接觸java集合類原始碼。所以一開始只寫了兩句程式碼來測試,畢竟原始碼學習是很緩慢的過程。只能慢慢的啃。在閱讀原始碼前,我是把ArrayList和AbstractArrayList都拷貝出來了(不拷貝出來是不能對原始碼修改的),而且在很多方法前都加了註釋方便閱讀。閱讀原始碼的環境採用IDEA。

接下來就以這兩句程式碼來追蹤除錯檢視 執行過程。一步步的學習原始碼。

          

打上兩個端點,點選dbug。這裡有一個警告,是因為往testArrayList裡面添加了元素但卻沒有做任何操作。

IDEA怎麼除錯相比大家都會,打上斷點後,點選右上的一隻綠色的蟲的行了。

這裡我們一步步的點選下一步。因為ArrayList有很多靜態變數,所以當第一次點選下一步的時候,會跳轉到原始碼靜態變數這裡來。

接著再點選下一步。由於我們測試語句的第一句是 new ArrayList()。我們在new的時候沒有指定容器大小。所以底層肯定會執行一個無參構造。很顯然,我們的猜想是正確的。

這裡要注意,當我們再點選下一步的時候,會去執行AbstractList,因為ArrayList繼承自抽象類AbstractLIst,在執行自己的建構函式時會先呼叫父類的無參構造。

此時我們再點下一步,會發現一個奇怪的現象,底層執行並沒有回到Arraylist的無參構造裡面繼續執行。而是去初始化一個變數modCount。這個modCount是什麼?前幾天我查了資料瞭解了一下。

該變數主要用來實現
* fail-fast機制的(fail-fast 機制是java集合(Collection)中的一種錯誤機制。
* 當多個執行緒對同一個集合的內容進行操作時,就可能會產生fail-fast事件)

接著再點選下一步。回到了AbstractList的建構函式,執行結束

然後再次點選下一步。這個時候回到了ArrayList的無參構造,這裡將DEFAULTCAPACITY_EMPTY_ELEMENTDATA賦給elementData。

elementData: ArrayList基於陣列實現,用該陣列儲存資料, ArrayList 的容量就是該陣列的長度
DEFAULTCAPACITY_EMPTY_ELEMENTDATA: 一個Object型別的空陣列例項
* - 當用戶沒有指定 ArrayList 的容量時(即呼叫無參建構函式),
* 返回的是該陣列

再次點選下一步,此時我們測試程式碼的第一句就已經結束了,接著開始執行add方法。

點選下一步。此時會在add方法內部執行一個函式esureCapacityInternal 。

size:ArrayList實際儲存的資料數量,一開始沒做任何操作,預設初始化為0

再次點選下一步,這裡會判斷elementData是否等於DEFAULTCAPACITY_EMPTY_ELEMENTDATA,即判斷是否使用者在建立容器的時候沒有指定容量。這裡肯定是True。此時會執行一個Math.max方法。這個方法的作用是:將DEFAULT_CAPACITY和mincapacity比較誰大。這裡DEFAULT_CAPACITY是10,而mincapacity是1,所以這裡會把10賦值給mincapacity。

DEFAULT_CAPACITY: 預設容量,預設大小是10

這裡再點選下一步,這裡會執行一個函式,引數為minCapacity

再點選下一步。這裡modCount自增。樓主,查過一個帖子,上面說凡是出現這個modCount操作的集合類。這個集合類都是執行緒不完全的,但是是否是這樣還需要之後去求證。

再次點選下一步。將minCapacity與element.length做比較,此處的minCapacity大小為10。element.length是0

所以顯然是true。

點選下一步。這裡將elementData.length賦給oldCapacity。然後將自身加上自身做右移運算。

如果新的容量還是比minCapacity小,那就把最小容量賦給它。

點選下一步。這裡對newCapacity進行判斷。是否比MAX_ARRAY_SIZE大。如果比其大還得執行hugeCapacity。這個函式裡面其實就是個判斷和三元運算,可以自己去看。這裡我們的測試語句是不會進hugeCapacity的。

MAX_ARRAY_SIZE :
陣列緩衝區最大儲存容量
* - 一些 VM 會在一個數組中儲存某些資料--->為什麼要減去 8 的原因
*  - 嘗試分配這個最大儲存容量,可能會導致 OutOfMemoryError(當該值 > VM 的限制時)
*   Integer.MAX_VALUE 值為正21億

再次點選下一步。接下來進行資料擴容,將newCapacity這個int型別的,值為10。讓陣列eleementData擴容至這個newCapacity大小。

接下來擴容就完成,開始彈棧,一步步的返回最開始的add函式。執行最終的把值新增到容器中。

這裡就是把 1 新增到 elementData[0]。然後把size+1。size為容器的實際大小,通俗點就是裡面實際裝了多少東西。

注意這裡要區分容器的實際大小和容器的容量,當我們實際大小遠小於容器的容量,是會造成空間浪費的。

最後返回True。

好了。到這裡,這就是兩句測試語句的執行過程和底層原始碼剖析。總結一下。

這兩句的底層執行過程就是:

載入靜態變數---->public ArrayList()---->protected AbstractList()丶int modCount = 0---->
public boolean add(E e)---->private void ensureCapacityInternal(int minCapacity)
---->private void ensureExplicitCapacity(int minCapacity)---->
---->private void grow(int minCapacity)

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