java集合-ArrayList中EMPTY_ELEMENTDATA與DEFAULTCAPACITY_EMPTY_ELEMENTDATA的區別


 源碼分析

    /**
     * 用於空實例的共享空數組實例。
     */
    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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM