ArrayList之初始化源碼

一、下酒菜

聊源碼之前,先看點關於ArrayList初始化的一些面試題

  • ArrayList的數據結構是怎樣的?
  • 爲什麼ArrayList中數組變量elementData,不使用private修飾?
  • new ArrayList()和new ArrayList(0)有什麼區別?
  • new ArrayList()時,它的數組長度是多少?
  • new ArrayList(9)時,它的數組長度是多少?
  • new ArrayList()時,add一個元素後,它的數組長度是多少?
  • new ArrayList(0)時,add一個元素後,它的數組長度是多少?
  • ArrayList中EMPTY_ELEMENTDATADEFAULTCAPACITY_EMPTY_ELEMENTDATA兩個變量都表示空數組,有何作用?

二、ArrayList中跟初始化相關的屬性

1. 空數組:EMPTY_ELEMENTDATA
    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};
2. 空數組:DEFAULTCAPACITY_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 = {};
  • 區分DEFAULTCAPACITY_EMPTY_ELEMENTDATA和DEFAULT_CAPACITY兩個空數組,是爲了知道在第一次執行add方法時,應該擴容多少。後面會提到
3. 默認初始大小
    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

注意,這個默認初始大小是指new ArrayList()時,第一次add元素時的擴容大小,而不是new ArrayList()時數組的大小!

4. 存儲數據的數組
    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access
  • 註釋中提到,當elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA時(即new ArrayList()時),第一次add元素時擴容爲DEFAULT_CAPACITY(10)。對應上面3中的介紹。
  • 不使用private是因爲方便嵌套類的訪問。這裏多提一下,語法層面上無論外部類屬性是不是private修飾的,內部類都可以訪問;但是如果外部類屬性被private修飾,內部類訪問該屬性時,編譯器會生成靜態的訪問方法來提供給內部類訪問。
  • 這裏解釋下EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA的區別,也就是上面2中的後續。當elementData爲EMPTY_ELEMENTDATA時第一次add後擴容爲1;當elementData爲DEFAULTCAPACITY_EMPTY_ELEMENTDATA時第一次add後擴容爲10。這樣有什麼好處呢?想不到,就擴大來想。如果十萬個ArrayList每個長度爲2就好了,爲什麼都初始化爲10呢?

三、ArrayList共有三個構造方法

1. 無參構造方法
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
  • 可以看到該無參構造方法,是給elementData賦值爲一個空數組;所以new ArrayList()時,它的數組長度是0
  • 此時空數組的值爲:DEFAULTCAPACITY_EMPTY_ELEMENTDATA
2. 入參僅有一個整型的構造方法
    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);
        }
    }
  • 如果參數爲0時,空數組的值爲:EMPTY_ELEMENTDATA
  • 如果參數大於0時,直接創建一個參數大小的數組。所以new ArrayList(9)時,它的數組長度就是9,不會改成10
3. 入參僅有一個Collection類型的構造方法
   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()不就是返回Object[]的嗎?爲什麼還要判斷一下呢? 原因註釋上也寫了,c.toArray可能不能正確放回Object[]。寫個返回其他類型的代碼就明白了

    private Object[] returnTest() {
        return new String[10];
    }

雖然方法聲明是Object[],但返回的引用可能是其他類型喲。再舉個實際情況的例子

    @Test
    public void constructCodeToArrayTest(){
        List<String> asList = Arrays.asList("asList", "aa");
        Object[] asListArray = asList.toArray();
        System.out.println(asListArray.getClass());// class [Ljava.lang.String;
    }

四、小結


把ArrayList的初始化源碼看完之後,在回頭看看上面的面試題是不是感覺很小兒科了?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章