從JDK源碼學習Arraylist

從今天開始從源碼去學習一些Java的經常使用數據結構,打好基礎:)html

Arraylist源碼閱讀:

jdk版本:1.8.0數組

首先看其構造方法:安全

構造方法一:

第一種支持初始化容量大小,其中聲明一個對象數組,賦值給this.elementdata數據結構

構造方法二:

第二種無參構造函數,即不指定初始容量大小,則默認賦值this.elementdata爲一個空的對象數組,可是由註釋能夠看到其無參構造實際上初始容量爲10多線程

 

在elementData的註釋中也說了該變量是實際存儲Arrylist數據的存儲結構,任何空的arraylist,當第一次被調用add放進元素時,將會擴充容量爲default_capacity也就是10函數

 

 

 

 

看看其add方法,由於arraylist也是有序的,所以加入的元素在列表尾部,在添加元素以前,調用ensureCapacityInternal,確保內部容量大小學習

在ensureCapacityInternal中將判斷當前的elementdata的值是否爲空數組,若爲空則賦值minCapacity爲默認容量和入口參數minCapacity的較大值,而後進一步調用ensureExplicitCapacity明確容量大小this

 

在ensureExplicitCapacity中,modCount自增,判斷當前最小容量和arraylist的實際元素個數差值若大於零,則調用grow函數來進行實際的容量擴充spa

 

 

擴容函數grow先取到當前arraylist的實際長度,而後將其擴大1.5倍,而後判斷該值和最小容量的大小,若擴充1.5倍小於所須要的最小容量,則賦值新的容量爲須要的最小容量,此時並判斷是否產生溢出狀況,也就是註釋裏面的overflow conscious mode的含義,因此arraylist不是無限擴容,看下其max_array_size的值.net

 

數組最大值爲integer.max_value-8,也就是2的31次-1-8

至於爲何要-8,這裏有些vm要存儲其最大值的大小須要八個字節,以下圖所示

 

 若是擴充的新容量比max還大,則調用hugeCapacity,判斷最小的容量和2的31次-1的大小,若大於則賦值max_value,不然說明此時最小容量介於max_value-8和max_value之間,則賦值爲max_value-8

 

而後調用Array.copyof將舊的arraylist中的值拷貝到新的擴充後的arraylist中,因此默認空數組的add操做後容量即爲10

構造方法三:

能夠傳遞任何實現了Collection接口的類,其調用collection的toarray方法返回一個對象數組,也就是將集合中的元素以對象數組形式返回,toarray的註釋裏也說明了這個方法是array和collection的橋樑

 

 

 爲了防止重寫toArray方法返回的並非對象數組,所以這裏判斷一下elementData的類是不是對象數組,若是不是的話,則將element中的數組copy到對象數組中

好比有MySubClass是MyClass的子類。
Collection<MyClass> myCollection;  //myCollection裏有不少元素。
Collection<MySubClass> mySubCollection;  //mySubCollection裏有不少元素。
ArrayList<MyClass> myList = new ArrayList<MyClass>(myCollection);
也能夠:
ArrayList<MyClass> myList = new ArrayList<MyClass>(mySubCollection);

意思就是這裏用extends e,來指定定義一個父類的arraylist,則其全部子類的集合都能放進該父類的arraylist,從而編譯器纔可以知道放入的元素都是知足?也就是,初始定義arraylist的類型聲明

關於線程安全:

 上面遺留了一個modcount++的自增操做的解釋,看一下jdk對modcount的解釋

 該參數是對arraylist容量大小修改的次數,也就是刪減元素改變大小時可能會使正常的迭代過程出現錯誤,那麼針對單線程而言,不存在又讀又寫,但在多線程狀況下,可能存在讀寫同時進行的操做,參考知乎一個很精簡明確的答案,看完真的是一目瞭然,若是結構發生變化則拋出ConcurrentModificationException

 

 經過調用上面這個方法來判斷是否結構發生變化,調用add remove時都將修改modcount,經過迭代時先保存一份modcount,若迭代過程當中再取modcount和保存的值不等則拋出異常

總結:

①.初始不指定容量時設置爲10

②.每次擴充爲實際長度的1.5倍與所需最小容量比較

③.arraylist是非線程安全的

④.其最大值爲2的31次-1

⑤.爲避免連續擴容消耗內存,能初始化容量大小盡可能指定容量

⑥.爲啥會非線程安全,由於方法內部並不是原子操做

 

參考:

https://zhuanlan.zhihu.com/p/72296421  hashmap

https://zhuanlan.zhihu.com/p/73283922  linkedhashmap

https://zhuanlan.zhihu.com/p/72463637 hashset

https://zhuanlan.zhihu.com/p/72156592 arraylist

https://www.jianshu.com/p/f174d49b391c

http://www.javashuo.com/article/p-qlfagxdt-eq.html 

http://www.javashuo.com/article/p-qzzjqkuw-dt.html 線程安全問題

相關文章
相關標籤/搜索