1. 程式人生 > >ArrayList原始碼分析筆記

ArrayList原始碼分析筆記

# ArrayList原始碼分析筆記 ### 先貼出ArrayList一些屬性 ```java public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable { /** * 系列化ID. */ private static final long serialVersionUID = 8683452581122892189L; /** * 預設初始化容量. */ private static final int DEFAULT_CAPACITY = 10; /** * 用於空例項的共享空陣列例項. */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * 共享的空陣列例項,用於預設大小的空例項。我們將此與EMPTY_ELEMENTDATA區別開來, 以瞭解新增第一個元素時需要膨脹多少。 */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * 儲存ArrayList的元素的陣列緩衝區。 ArrayList的容量是此陣列緩衝區的長度。新增第 一個元素時,任何具有elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空 ArrayList都將擴充套件為DEFAULT_CAPACITY。 */ transient Object[] elementData; /** * ArrayList的大小(它包含的元素數). * * @serial */ private int size; ``` 以上屬性註釋都已經被翻譯成中文,通過這些註釋,我們大概能瞭解到的一些有價值的資訊 - **ArrayList底層資料結構是一個Object陣列** - **ArrayList的預設初始化容量為10** - **一個空的ArrayList集合在新增第一個元素時被擴容為10** - **ArrayList的大小是通過一個名為size的int變數儲存的** 原始碼繼續往下看,先看ArrayList的建構函式 ```java public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } /** * 通過這裡可以看出,當呼叫ArrayList的無參建構函式時,這個ArrayList的Object陣列被初始化為 DEFAULTCAPACITY_EMPTY_ELEMENTDATA=10 */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * Constructs a list containing the elements of the specified * collection, in the order they are returned by the collection's * iterator. * * @param c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */ public ArrayList(Collection c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } } ``` 可以看到ArrayList一共有三個建構函式,一個無參構造,兩個有參構造。 當我們在建立一個Arraylist集合時,如果不指定容量,也就是呼叫無參建構函式,那麼這個Arraylist會被初始化為10. ```java public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } ``` 另外注意到另外兩個有參構造,一個引數為``int initialCapacity``,也就是我們指定的初始化容量,這裡對這個initialCapacity進行了一些簡單的驗證 ```Java public ArrayList(int initialCapacity) { if (initialCapacity > 0) {//initialCapacity > 0 this.elementData = new Object[initialCapacity];//將一個容量為initialCapacity的新Object陣列賦給elementData。 } else if (initialCapacity == 0) {//initialCapacity==0 this.elementData = EMPTY_ELEMENTDATA;//將EMPTY_ELEMENTDATA這個空object賦給elementData } else {//其他情況,也就是小於0,直接拋異常 throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } ``` 另一個有參構造 ```java public ArrayList(Collection c) { elementData = c.toArray();//將Collection集合轉化為陣列,然後賦給elementData這個object陣列 if ((size = elementData.length) != 0) {//如果這個集合長度不為0 // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class)//如果集合轉化後的陣列不是Object陣列 elementData = Arrays.copyOf(elementData, size, Object[].class);//轉化為Object陣列 } else {//其他情況,也就是elementData的長度為0嘛 // 替換為EMPTY_ELEMENTDATA空的陣列 this.elementData = EMPTY_ELEMENTDATA; } } ``` 可以看到這個建構函式,它是直接把一個Collection丟了進去,這也就是說,**在new一個ArrayList時我們可以把一個Collection集合放到引數列表中。** 接下來再來看ArrayList的add方法 ```java /** * 將元素新增到列表的末尾 * * @param e 要被追加到list中的元素 * @return true
(as specified by {@link Collection#add}) */ public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e;//將元素e新增到陣列中,下標為size++,其實也就是size,然後新增後size+1, return true; } ``` 這裡就兩行程式碼,呼叫``ensureCapacityInternal``這個方法,並且引數為size+1。點進去看下``ensureCapacityInternal()``這個方法 ```java /* 通過這個方法我們可以看出,當我們第一次呼叫add方法的時候,elementData陣列的size為0,那麼size+1就為1,所以minCapacity也為1,這裡先通過一個if判斷,判斷minCapacity是不是一個空的object陣列,如果是的話,minCapacity就取DEFAULT_CAPACITY和minCapacity的最大值,也就是10嘛 */ private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } //判斷完之後進入了這個方法,同時引數為minCapacity,如果是第一次呼叫add方法的話引數為minCapacity就是預設大小10,不是第一次呼叫的話,就是size+1, ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; //這個grow()就是擴容函式,現在minCapacity的話,還是一樣的分析,如果第一次呼叫add方法的話就是10,同時elementData.length為0,不是第一次呼叫的話minCapacity就是size+1,elementData.length也就是陣列的長度為size if (minCapacity - elementData.length >
0) grow(minCapacity); } ``` 讀完這一段原始碼,可以得出以下結論 - 當我們建立ArrayList時,如果不指定ArrayList的大小,那麼第一次呼叫add方法時,底層會呼叫擴容方法grow()對Arraylist進行擴容 - 當ArrayList裡面已經存滿了元素的時候,再呼叫add方法,此時會底層會呼叫grow方法進行擴容,比如ArrayList的大小為10,裡面也已經有10個元素了,當我們再呼叫add方法向裡面新增元素的時候,此時Arraylist會觸發擴容。 接下來我們再來看下grow方法,瞭解一些Arraylist到底是怎麼樣擴容的 ```java private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >
> 1); //如果陣列當前長度+陣列當前長度/2 < 陣列當前元素個數+1 if (newCapacity - minCapacity < 0) //就把陣列當前元素個數+1賦給newCapacity newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 最後我們可以看到,這裡進行了一個複製操作,將elementData裡面的元素,複製到一個新的長度為newCapacity的Object陣列,然後再把它賦給elementData elementData = Arrays.copyOf(elementData, newCapacity); } ``` 這段程式碼應該很清晰吧,grow方法傳進來的引數就是上面minCapacity,第一次呼叫add方法時是10,其他時候呼叫時是size+1(當前儲存元素個數+1) 這裡簡單來說就是將elementData當前長度給oldCapacity,然後newCapacity = oldCapacity + oldCapacity >> 1(左移操作,相當於除於2),如果oldCapacity = 10的話,newCapacity = 10 +(10/2),也就是15。通過以上程式碼,不難看出,ArrayList的擴容方法實際上就是將底層Object陣列複製到了一個新的長度更長的數組裡面去,而這個新陣列的長度是``原來的陣列長度+原來陣列長度的二分之一``,其實就可以說了擴容了1.5倍。 通過對以上原始碼進行分析,我們可以得出以下結論了 總結: - **ArrayList底層資料資料結構是一個Object型別的陣列** - **ArrayList的預設初始化容量為10** - **當我們在建立ArrsyList時不指定ArrsyList初始大小,第一次呼叫add方法時擴容為初始化大小10** - **ArrayList的大小是通過一個名為size的int變數儲存的** - **ArrayList有一個建構函式允許Collection集合作為引數,並且會將這個Collection集合轉化為陣列** - **ArrayList在新增第``elementData.length+1``個元素時會發生擴容,比如陣列長度為10,在新增第11個元素時擴容** - **ArrayList擴容大小為原來的1.5倍,底層實現是通過``原來陣列長度+原來陣列長度左移1位``完成,而不是直接通過乘法**