1. 程式人生 > >【JDk原始碼解析之一】ArrayList原始碼解析

【JDk原始碼解析之一】ArrayList原始碼解析

1.ArrayList的繼承關係如圖所示:


2.巨集觀上說,ArrayList是基於動態陣列實現的,陣列具有按索引查詢的特性,所以訪問很快,適合經常查詢的資料。

3.具體原始碼解析。

為什麼說ArrayList是動態陣列,這個可以看它的建構函式。如下圖所示,有兩個構造方法,存放元素的elementData是一個數組,同時被初始化。ArrayList的放的元素就是放     在這個數組裡。


ArrayLIst的預設初始化容量是10,這個是容量,並不是ArrayList的初始化大小,更不能等同於size,而是elementData[]這個陣列的大小,而size指的是數組裡面存放有資料的   數量,也就是list的大小。直接new ArrayList,,然後呼叫相應的size()方法,肯定返回的是0、

   

當容量不夠時候(ensureCapacityInternal(size + 1)),會呼叫相應的擴容的方法(private void grow(int minCapacity))。下面再講add方法時候,會詳細講解。

3.1 往連結串列增加元素:add方法。

ArrayList提供了兩種add方法,add(E e)  和  add(int index, E element)。


add(E e)是直接往陣列最後增加資料,而後者則是往指定的index增加資料。在增加的過程中,根     據索引來增加資料的add方法,首先要判斷index越界的問題。這個問題看起來很簡單,確實很重要,完美筆試的時候,就是沒寫這個,通過率一直不到100%。接下來,就是兩者 ensureCapacityInternal(size + 1)。看名字也很容易知道,這個方法是為了確保容量夠,接下來來看這個方法是怎麼做的。如下圖所示,這個方法的引數minCapacity接受傳入的(size+1),然後判斷這個陣列elementData是否為空。這個陣列為空的時候,返回DEFAULT_CAPACITY和minCapacity的最大值。這個時候就可以理解,為什麼說預設容量是10。因為他每次增加元素時候,是和DEFAULT_CAPACITY來比較的。不夠的時候才會擴充。接下來呼叫了ensureExplicitCapacity方法。如果資料的數量超過了elementData陣列 長度,說明容量不夠了,就執行grow方法,擴容。


接下里看看,它是怎麼擴容的。進入grow(int minCapacity)方法,這個minCapacity指的是(size+1)。如下圖所示,先將現在的容量放入oldCapacity,然後將其右移1位,相

當於除以2,然後在加上原來的容量,其實就是1.5倍。說白了就是每次擴充原來容量的一半。擴充完畢後,檢查這個新容量是不是太大了。如果比MAX_ARRAY_SIZE大,那就將其設定為Integer.MAX_VALUE. 那麼問題來了,MAX_ARRAY_SIZE是多大?幹啥的?


MAX_ARRAY_SIZE是在該類裡面定義的一個常量,定義和英文解釋如下。可以看到它比Integer.MAX.VALUE少8。那麼新的問題來了,為什麼減去8?首先英文的註釋寫的很清楚了,為了避免OOM異常。但是為什麼減去8,減去4行不?減去2呢?

我們知道,陣列除了存放資料外,還有一個length屬性,說白了,減去8是為了存陣列的長度。


這些都做完後,呼叫Arrays.copy()方法,將資料進行拷貝。從這裡也可以看出擴容是很影響效能的。

3.2 移除資料 :remove()方法。

ArrayList可以有兩種方式進行資料的刪除,即按照索引和按照元素來進行刪除,兩個方法如下所示。這兩個方法都不難,最後都是通過copy方法就行資料的移動。


3.3 其他有意思的方法:addAll()方法

    addAll()方法的所用是Array List可以方便的直接將list作為引數傳入,然後增加。具體看如下的程式碼


4.總結

ArrayList是基於動態陣列實現的,隨機存取快,但是在增刪時候,需要陣列的拷貝複製,這個時候明顯劣勢。

它不是執行緒安全的。

它能存放null值。

ArrayList的預設初始化容量是10,每次擴容時候增加原先容量的一半,也就是變為原來的1.5倍