Arraylistd底層原理原始碼分析(轉
ArrayList簡介
ArrayList是我們在開發中非常常用的資料儲存容器之一,其底層是陣列實現的,我們可以在集合中儲存任意型別的資料。ArrayList又是執行緒不安全的,這在接下來程式碼分析的過程中會有體現。ArrayList非常適合對元素進行查詢,效率非常高。
原始碼分析
既然是分析原始碼,那我們先從建構函式開始。我們常用的建構函式有兩個,分別是有參和無參的,如下圖
無參的建構函式十分簡單,只有一行程式碼。elementData是一個Object型的陣列,DEFAULTCAPACITY_MPTY_ELEMENTDATA是一個空的Object型陣列。再來看看有參的,其實引數的含義就是這個集合的容量,程式碼很簡單,一看就懂,如果指定了長度的話,會讓元素容易elementData指向一個新的陣列,長度為傳入的值,而如果傳入的值為0的話,則指向EMPTY_ELEMENTDATA。EMPTY_ELEMENTDATA與DEFAULTCAPACITY_MPTY_ELEMENTDATA一樣,都是一個空的Object型的陣列,那麼區別在哪裡呢?接下來就是重點。要知道,我們聲明瞭一個集合,目的是向其中新增元素,而區別就在新增的過程中提現出來了,請看新增方法,如下圖
在新增元素之前,首先需要確保容量是否夠用
區別就在這裡,如果我們一開始宣告的是一個空參的建構函式,那麼程式碼會走進第一個if語句,傳入的minCapacity是1,那麼最終計算得出minCapacity是等於DEFAULT_CAPACITY的,而DEFAULT_CAPACITY的值是10。也就是說,如果我們添加了一個預設的無參建構函式,在新增時,陣列會將預設的陣列長度變為10。說到這裡,第一個使用技巧就出現了,如果我們很確定的知道我們要儲存元素的數量,最好在宣告集合的時候傳入容量值。試想,如果我們只需要儲存3個元素,而我們聲明瞭一個空參的建構函式,那麼集合的長度會是10,也就是說,陣列有7個長度的空間被浪費了,這就是對記憶體的一種浪費。
接著往下走,之後會判斷陣列的容量是否夠用,如果夠用,那麼不需要擴容,反之,則進行擴容,判斷擴容程式碼沒什麼,很簡單,但是擴容怎麼辦呢?陣列一旦宣告不是不能更改了嗎?原始碼中是將陣列進行copy,而copyOf的底層原始碼,是聲明瞭一個新的陣列,然後將原有的陣列內容全複製進去,這樣,就在不影響原有資料的基礎上進行了陣列擴容。說到這裡,第一個使用技巧還是適用的。試想一下,我們需要儲存1000個元素,而一開始預設長度是10,那麼集合需要很多次擴容,每次擴容是上一次容量的1.5倍,每次擴容還要進行復制。如果不事先宣告一個長度的話,使用效率會大大降低,即便是不知道具體數字,也可以指定一個大概的容量。
新增的方法說完了,接下來簡單說一下刪除方法,如下圖
刪除是每次進行陣列複製,然後讓舊的elementData置為null進行垃圾回收,程式碼很簡單,一看就懂,但是我們可以從原始碼中去發現使用技巧。
查詢的方法就更簡單了,直接返回查詢的對應陣列中的值。
總結
1.在宣告時儘量指定長度。
2.ArrayList底層是陣列,陣列是適合查詢的,因為陣列每個元素的記憶體空間是固定的,每次查詢時,只需要去查詢對應位置的記憶體空間,就可以很快找到相應的值。而陣列不擅長的是新增和刪除。試想,集合長度是100000,而我們在第2個位置添加了一個元素,導致的結果是從第3個開始後面每一個元素都要往後串一個元素記憶體空間那麼大的位置。刪除剛好相反,是向前串一個位置,這樣的效率是很低的,元素越多,效率越低。而頻繁的新增和刪除,適用連結串列,LinkedList,可以參考我的另一篇部落格