java基礎提升之ArrayList

        這一篇開始集合的總結,通過前兩篇文章(fail-fast 以及 Spliterator)的鋪墊,咱們能更快的進入集合類的學習。本篇是集合類的開篇,也是比較簡單的一篇。此次咱們主要來看看ArrayList類的實現。看看他是如何存儲元素以及擴容的。算法

結構分析

RandomAccess

        首先,咱們來看看ArrayList繼承了啥,實現了啥數組

        這裏咱們不對AbstractList進行講解,不過就是一些List的基本操做。咱們重點看一下RandomAccess。dom

         查看RandomAccess的源碼:性能

        咱們發現他什麼都沒幹,那麼這個接口是幹什麼的呢? 其實,從名字咱們也能猜出來他是幹啥的RandomAccess 即隨機訪問,源碼中是這麼介紹他的學習

        一個標記接口,List實現它表示本身支持快速的(一般是恆定時間)隨機訪問。這個接口的主要目的就是容許算法去提供一個更好的執行性能當決定使用隨機或者順序訪問的時候。3d

        上面的隨機訪問即便用索引 index去訪問,順序訪問則是使用Iterator這樣迭代的方法去訪問。cdn

        因此當算法去決定是否是使用隨機訪問比順序訪問的好的時候就能夠用 instanceof RandomAccess 去判斷。對象

存儲結構

        上面咱們說這個ArrayList隨機訪問的性能好,那麼爲何隨機訪問性能好呢? 咱們來看看ArrayList中的幾個屬性。blog

  1. elementData: 這個就是ArrayList存儲元素的地方,ArrayList的容量就是這個數組的長度。 其實之因此ArrayList的隨機訪問性能好就是由於其底層存儲數據的是一個數組。按下標訪問性能高效是數組自然的優點。
  2. EMPTY_ELEMENTDATA: 用於空實例的共享空數組實例。 當咱們建立初始容量爲 0 或者根據一個空的Collection去建立一個ArrayList 即調用 new ArrayList(0);或者new ArrayList(Collection<? extends E> c);(注意這個c.toArray().length==0)的時候elementData會被賦予EMPTY_ELEMENTDATA。
  3. DEFAULTCAPACITY_EMPTY_ELEMENTDATA: 用於默認大小的空實例的共享空數組實例。 之因此將這個與EMPTY_ELEMENTDATA區分開來,是由於方便在添加第一個元素時知道要擴容多少。 當調用 new ArrayList();的時候會將此賦予elementData;
  4. DEFAULT_CAPACITY: 默認初始容量,當不指定初始容量時使用,默認是10,即不指定初始容量的狀態下,ArrayList的容量是10,超出此值便會進行擴容操做。
  5. size: 沒什麼特別的,記錄ArrayList存儲元素的數量。可是其與ArrayList的容量並不等價。ArrayList中有一個方法,代碼以下:

意思就是調用這個方法後,elementData的數組的長度就會等於size的大小,即把數組的大小調整爲存儲當前數量元素實際須要的大小。繼承

        通過上面的分析,咱們得出了ArrayList的存儲結構是一個數組的形式

基本方法實現

添加元素

對於添加來講不過就是add方法,set方法。咱們實際看一下代碼:

咱們先無論ensureCapacityInternal這個方法,這個是擴容機制的開始方法,下面咱們會再提。 其實對於插入來講就是對數組的操做,有意思的是,在add(int index,E element)中使用了System.arraycopy方法來對數組進行移位操做。這是一個淺複製,有興趣額的同窗本身查查看吧。 刪除操做其實都同樣,就不作贅述了。

擴容機制

        ArrayList的擴容機制其實挺簡單的,咱們先從代碼入手看看,他是怎麼完成這個擴容的。上面提到ensureCapacityInternal方法時擴容的開始,其實在每個往elementData裏面添加元素的方法中都先調用了這個方法,參數是增長當前元素的數量加上須要添加元素的數量。即ArrayList在添加元素的時候會檢查是否須要擴容。咱們先看看這個方法:

        咱們能夠看到,這個方法先判斷了一下elementData是否等於DEFAULTCAPACITY_EMPTY_ELEMENTDATA,即咱們上面提到的使用new ArrayList();生成的ArrayList對象中elementData賦予的值,也就是檢查一下,這個ArrayList是否是默認容量如今是DEFAULT_CAPACITY。

        這個minCapacity的含義就是添加某個多多個元素後,elementData最小的長度。

以後這個方法又調用了ensureExplicitCapacity方法,咱們如今看一下這個方法的內容:

        modCount做用於快速失敗機制,若是不瞭解能夠看一下這篇文章

        以後判斷了一下這個minCapacity的大小是否大於elementData的長度,即elementData的大小是否是已經不知足增長某個或者多個元素的要求了。

        若是不知足則調用grow方法。如今就到了擴容的核心了,咱們先看一下grow方法的內容:

其實就是對應該擴容後elementData的大小進行判斷,默認增加爲原來大小的1.5倍。由於是使用位移的方法計算原來elementData長度的一半,因此有可能oldCapacity>>1的結果爲負數,這時候直接擴容爲須要的大小。後面的不解釋了,很簡單。

要注意一點,因爲elementData是數組,而數組的大小是不能改變的,因此擴容是從新建立了一個數組。

總結

  1. ArrayList底層存儲結構是一個數組
  2. ArrayList擴容默認增加爲原來的1.5倍,若是還不知足須要則直接增加爲須要的長度。
  3. 按數組下標訪問元素-get(i)\set(i,e)的性能很高,這是數組的基本優點。
  4. 直接在數組末尾加入元素-add(e)的性能也很高,但若是按下標插入、刪除元素-add(i,e)\remove(i)\remove(e),則要用System.arraycopy()來移動部分受影響的元素,性能就變差了,這是基本劣勢。
相關文章
相關標籤/搜索