天天看點

# 深入了解ArrayList集合的初始容量和初始容量為0的兩種擴容機制(1.8版本)深入了解ArrayList集合的初始容量和初始容量為0的兩種擴容機制(1.8版本)

深入了解ArrayList集合的初始容量和初始容量為0的兩種擴容機制(1.8版本)

網絡上對于ArrayList集合的空參構造是否為0,存在不同的的看法。對此,分析了源碼,有以下見解:
1.空參構造,集合初始容量必定為0,添加一個元素,擴容為10。
2.有參構造,參數為0和集合長度為0時,初始容量為0,添加一個元素擴容為1,再添加一個元素擴容為2,再添加一個元素擴容
為3,再添加一個元素擴容為4,再添加一個元素擴容為6...
           
  • 版本不同
  • ArrayList的三種構造方法
  • ArrayList()的初始容量和擴容
  • ArrayList(int initialCapacity)的初始容量擴容
  • ArrayList(Collection( extends E) c)(尖括号顯示不出來,用()代替)
  • ArrayList集合的擴容公式

版本不同

JAVA對于ArrayList集合進行了優化,目前所知,優化了空參構造的初始容量和擴容公式,至于是1.7還是1.8版本開始,
就不清楚了。在1.6版本之時,該集合的空參構造的初始容量,确實是10,源碼自己找。
該文的源碼以JDK1.8為主。
           

ArrayList的三種構造方法

public ArrayList(int initialCapacity)
public ArrayList()
public ArrayList(Collection<? extends E> c)
           

public ArrayList()的初始容量和擴容

首先,分析空參構造的初始容量。
在1.8裡面定義了三個Object[]:
           
1.用于空執行個體的共享空數組執行個體。
private static final Object[] EMPTY_ELEMENTDATA = {}
           
2.用于預設大小的空執行個體的共享空數組執行個體。
   該數組與EMPTY_ELEMENTDATA差別,但都為空,以便知道當添加第一個元素時,該數組應該于何時擴容多少。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
           

而這兩個數組,有什麼關系呢?

第一個數組運用于有參構造方法的參數為0或者集合長度為0的時候,而第二個數組運用于空參構造的時候。

這兩個數組一定要差別,對于以後的擴容造成影響。

ArrayList的底層是由(transient Object[] elementData)實作的; 該數組是存儲ArrayList元素的數組緩沖區。ArrayList的容量是這個數組緩沖區的長度。
    任何的elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空集合,當添加第一個元素時,将被擴充到DEFAULT_CAPACITY。
           
  • 初始容量
要知道空參構造的初始容量,要對空參構造的源碼進行分析:
	public ArrayList() {
        this.elementData =
        DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    這樣看空參構造,初始容量就是0。
           
  • 擴容機制
那麼當添加一個元素的時候,集合的容量會擴容到預設容量為10,也可能為大于10的值,視情況而定。一般預設一次隻添加一個元素。
private int newCapacity(int minCapacity) {
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity <= 0) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                return Math.max(DEFAULT_CAPACITY, minCapacity);
                //這裡就定義了當
                //DEFAULTCAPACITY_EMPTY_ELEMENTDATA數組添加元素的情況,若是第一次添加:1.隻添加一個元素,
                //那麼容量就擴容為DEFAULT_CAPACITY=10;2.要是添加的元素大于該預設容量,則以該元素個數為
                //擴容後的容量。
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return minCapacity;
        }
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }
           

public ArrayList(int initialCapacity)的初始容量和擴容

  • 初始容量

這裡就讨論當參數為0的時候,該集合的初始容量也為0。

public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
            //當該參數為0,底層數組為EMPTY_ELEMENTDATA,那麼該數組的長度為0,集合初始容量則為0。
        } else {
            throw new IllegalArgumentException("Illegal    Capacity: "+initialCapacity);
        }
    }
           
  • 擴容機制

    加進一個元素,擴容為1。

    源碼分析:

//add源碼  
public boolean add(E e) {
        modCount++;
        add(e, elementData, size);//調用add(E e, Object[] elementData, int s)方法
        return true;
    } 
//添加一個元素,調用此方法  
private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();// 數組長度為0,繼續調用grow()方法
        elementData[s] = e;
        size = s + 1;
    }
private Object[] grow() {
        return grow(size + 1);//grow()調用grow(1)
    }
private Object[] grow(int minCapacity) {
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
                                           //擴容方法newCapacity(minCapacity)
    }
    //擴容
private int newCapacity(int minCapacity) {
        int oldCapacity = elementData.length;//oldCapacity=0
        int newCapacity = oldCapacity + (oldCapacity >> 1);//newCapacity=0
        if (newCapacity - minCapacity <= 0) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                return Math.max(DEFAULT_CAPACITY, minCapacity);
                //DEFAULTCAPACITY_EMPTY_ELEMENTDATA空參構造的數組;
                //抱歉這是為了空參構造設計的,參數為0和集合長度為0對應的數組是EMPTY_ELEMENTDATA。
            if (minCapacity < 0) 
                throw new OutOfMemoryError();
            return minCapacity;//上述條件都不符合,那麼就傳回1.
        }
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity//當newCapacity - minCapacity > 0,且小于最大的數組長度,就傳回原數組的1.5倍
            : hugeCapacity(minCapacity);
    }
           

是以當添加一個元素進去時,該集合擴容為1。

public ArrayList(Collection(? extends E) c)

該構造方法與 ArrayList(int initialCapacity)方法有一樣的地方:當參數集合長度為0時,該集合的初始容量也為0。

elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // defend against c.toArray (incorrectly) not returning Object[] 
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
     //當該集合為空時,同 ArrayList(int initialCapacity)方法的參數為0情況,集合的初始容量為0。
           

擴容情況參照ArrayList(0)的情況,同樣。

ArrayList集合的擴容公式

1.6版本的擴容公式:

數組原長度*3/2+1

.

1.6之後的擴容公式:

oldCapacity + (oldCapacity >> 1)

因為位運算快。

之是以寫此博文,是因為,看見一個拿着1.8版本的源碼,睜眼說瞎話,說空參構造的初始容量為10,這簡直就是害人。不平之下
, 按照自己對于源碼的見解,進行分析,望能幫助到需要之人。
若有不足之處,望批評指正。