源碼分析
/** * 用於空實例的共享空數組實例。 */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * 用於默認大小的空實例的共享空數組實例。 * 我們將其與EMPTY_ELEMENTDATA區分開來,以了解添加第一個元素時擴容多少。 * MARK:無參構造函數 使用該數組初始化 與EMPTY_ELEMENTDATA的區別主要是區分作用,用來減少空數組的存在,優化內存使用 1.8后的優化 */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
根據注釋說明這兩個是用來共享給空數組的,無參構造函數的空數組會用DEFAULTCAPACITY_EMPTY_ELEMENTDATA賦值,有參構造函數的空數組會用EMPTY_ELEMENTDATA賦值
再看三個構造函數
/** * 構造具有指定初始容量的空列表。 * @param initialCapacity 列表的初始容量 * @throws IllegalArgumentException */ 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); } } /** * 構造一個初始容量為10的空列表。 * MARK: * 這里其實是賦值了一個共享的空數組,數組在第一次添加元素時會判斷elementData是否等於DEFAULTCAPACITY_EMPTY_ELEMENTDATA,假如等於則會初始化容量為DEFAULT_CAPACITY 也就是10 * 符合 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的注釋說明 * private static int calculateCapacity(Object[] elementData, int minCapacity) 這個方法里可以看出 * */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * 構造一個包含指定元素的列表集合 * @param c 要將其元素放入此列表的集合 * @throws NullPointerException if the specified collection is null */ public ArrayList(Collection<? extends E> c) { // 這里說明所有的 Collection 都可以用數組來承載 // 這個步驟可能會拋空指針 NullPointerException elementData = c.toArray(); if ((size = elementData.length) != 0) { //必須是Object數組 if (elementData.getClass() != Object[].class) // 然后再copy一份到 elementData 並不是引用 所有改變不會影響到原先的Collection elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // 有參構造函數 當初始化為空數組時 賦值為 EMPTY_ELEMENTDATA this.elementData = EMPTY_ELEMENTDATA; } } /** * 這里屬於針對初始化時的擴容判斷 當為DEFAULTCAPACITY_EMPTY_ELEMENTDATA 時說明是無參構造函數創建的,則可以直接擴容為DEFAULT_CAPACITY也就是10為初始容量 */ private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }
從3個構造函數里可以看出,DEFAULTCAPACITY_EMPTY_ELEMENTDATA賦值,有參構造函數的空數組會用EMPTY_ELEMENTDATA賦值,在增加元素時會先做擴容判斷調用calculateCapacity方法,假如elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA就會直接擴容為默認容量10。空的ArrayList要在第一次添加元素時才會真正初始化容量。
我們再對比JDK1.7的三個構造方法
public ArrayList(int initialCapacity) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); // 這里直接new一個空數組 this.elementData = new Object[initialCapacity]; } public ArrayList() { super(); this.elementData = EMPTY_ELEMENTDATA; } public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); size = elementData.length; // c.toArray might (incorrectly) not return Object[] (see 6260652) // 這里實際上也相當於new了一個空數組 當c.toArray() 為空數組時copy了一份空數組 if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); }
以上JDK1.7的三個構造方法可以看出,當應用中存在很多空ArrayList同時也會new很多空數組,浪費內存空間
總結:
EMPTY_ELEMENTDATA用在有參構造函數當初始容量為0時共享賦值用,DEFAULTCAPACITY_EMPTY_ELEMENTDATA 用在無參構造函數賦值用
兩者都是用來減少空數組的創建,所有空ArrayList都共享空數組。兩者的區別主要是用來起區分用,針對有參無參的構造在擴容時做區分走不通的擴容邏輯,優化性能。
在無參構造函數創建ArrayList時其實創建的是一個容量為0的數組(DEFAULTCAPACITY_EMPTY_ELEMENTDATA 標識),只有在第一次新增元素時才會被擴容為10