1:代碼解讀和分析
1.1:構造方法分析
1:
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); } }
指定初始化容量的構造方法,當initialCapacity大於0時,立即new Object[initialCapacity];在一開始的時候就有了指定大小的數組在ArrayList內部;當initialCapacity==0時,則將EMPTY_ELEMENTDATA給elementdata,其中EMPTY_ELEMENTDATA是一個空數組。
這種情況下,size會是幾?
2:
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
第二個構造方法是無參構造方法。在這里直接將this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;其中DEFAULTCAPACITY_EMPTY_ELEMENTDATA和EMPTY_ELEMENTDATA一樣如下
/** * Shared empty array instance used for empty instances. */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
都是用private static final Object[]定義的兩個空數組。
3:
public ArrayList(Collection<? extends E> 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; } }
第三種構造方法是用通過放入實現Collection接口的容器來當作該構造方法的初始數據,通過toArray方法將內部的數據拿出來,給
elementData的方法來構建一個ArrayList出來。如果形參集合的數據長度為0,還是要將EMPTIYDATA數組賦給elementData的;
若源集合內的數組長度不為0后,在判斷數組類型,如果不為Object基本類型的化,則重新通過Arrays.copyOf方法創建一個新的數組,數據,長度不變,類型設置成Object類型。
注意:這里的size也已經在構造方法中更新了。
1.2:add方法分析
默認在數組尾部增加一個對象。
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
在add放法內部,使用ensureCapacityInternal(size + 1);方法,下面我們看一下該方法的作用。
private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); }
方法名字為:保證內部容量,該方法內部又通過calculateCapacity方法來計算最小的容量,輸入的minCapacity是size+1,即在增加數據之前來引入當前數據所用的最小的數據長度+1為計算標准進行判斷。
private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }
上面是calculateCapacity方法的內部的功能,分析如下如果當前ArrayList是通過無參的構造方法創建,且當前是第一次add數據,則返回默認的的DEFAULT_CAPACITY和minCapacity的較大值。其中前者是10,則在第一次add數據,當size+1=1小於DEFAULT_CAPACIT時,都回返回10。顯然當不是第一次add數據時,在這里的if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ,顯然不會成立了,則返回minCapacity!
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
經過calculateCapacity方法返回的結果送到ensureExplicitCapacity(int minCapacity)方法中,當第一次add數據時,經過calculateCapacity返回的結果是10,即默認的值,則會執行grow(minCapacity)方法。
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
在grow方法中,將原始數組長度保存在oldCapacity中,將newCapacity取值為oldCapacity的1.5倍。那如果newCapacity小於輸入的minCapacity,那么就會將minCapacity的值給newCapacity,則根據newCapacity的大小和原始的elementData通過Arrays.copyOf方法創建一個新的數據即可!
總結:通過無參ArrayList構造函數創建的ArrayList的內部數組是指向DEFAULTCAPACITY_EMPTY_ELEMENTDATA,在第一次add數據時,會通過上面的步驟,創建一個大小為10的elementData的數據在內部!通過指定大小的
1.2.1:非首次add
經過上面分析,我們再來分析一下第二次add數據的情況。此時ensureCapacityInternal(size + 1); size = 1,則ensureCapacityInternal(2),則calculateCapacity(Object[] elementData, int minCapacity),中minCapacity為2,直接返回2。再進入
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
其中,minCapacity為2,顯然小於elementData.length,所以不會擴容!
1.2.2:數組已滿時add
經過上面分析,我們再來分析一下在內置的數據滿的時候的add數據的情況,按照1.2.1中分析的那樣,再
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
中如果minCapacity大於elements.length時會進行grow,按照原來數組長度的1.5倍進行,擴容。
2:總結
1:ArrayList在使用無參構造方法創建時,在創建后到add前,其內部是一個空數組,當add第一個數據后,內部數組變成長度為10,當內部數組已滿時,就變成原來數組長度的1.5倍!
2:使用指定長度,會立即創建一個指定大小的數據在內部。
3:指定集合參數的構造方法是,會將根據源集合數據創建一個長度,數據與源集合相等的Object類型的內部數組。
3:對比LinkedList
LinkedList底層是用雙向鏈表實現的,所以它對元素的增加、刪除效率要比ArrayList好;它是一個雙向鏈表,沒有初始化大小,也沒有擴容的機制,就是一直在前面或者后面新增就好。