Arraylist的擴容機制


點贊再看,養成習慣,微信搜索「小大白日志」關注這個搬磚人。

本文在公眾號文章已同步,還有各種一線大廠面試原題、我的學習系列筆記。

arraylist每次添加元素時都會檢查是否需要擴容:arraylist第一次添加元素時,賦予arraylist默認容量10,再往里面添加元素(所以arraylist默認容量10並不是初始化的時候賦予的,而是無參構造第一次添加元素的時候賦予的);以后每次添加元素前先檢查當前元素個數是否已經達到容量上限,若是則先以1.5倍*原容量上限進行擴容再添加元素,如下:

  • arraylist中的初始變量
//當首次創建arraylist是無參構造,即沒有指定初始容量時,賦予該默認數組給arraylist
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//當首次創建arraylist是有參構造,即指定了初始容量,但初始容量指定為0時,賦予該默認數組給arraylist
private static final Object[] EMPTY_ELEMENTDATA = {};
//用於arraylist中實際存放元素的數組,注意此變量是transient修飾的,不參與序列化
transient Object[] elementData;
//數組元素的實際個數,默認0;不同於elementData.length,elementData.length是數組長度
private int size;
  • arraylist的三個構造函數
(1)默認無參構造:
public ArrayList() {
	this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
(2)指定了長度的有參構造
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];//指定長度>0,直接賦予指定長度的數組
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;//指定長度為0,賦予默認的長度為0的數組
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    }
}
(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;
    }
}
  • arraylist添加元素,兩種方法
//把元素添加到數組尾部
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 檢查是否需要擴容,size為當前實際元素個數,size+1為添加元素后需要的實際最小空間;第一次添加元素時size為0,傳入1
    elementData[size++] = e;//檢查完再往里面添加元素
    return true;
}
//把元素添加到數組特定的下標處
public void add(int index, E element) {
    rangeCheckForAdd(index);
    ensureCapacityInternal(size + 1);   // 檢查是否需要擴容
    System.arraycopy(elementData, index, elementData, index + 1,size - index);//檢查完先拷貝元素:把元素后移
    elementData[index] = element;//再添加元素
    size++;
}
  • 檢查是否需要擴容
private void ensureCapacityInternal(int minCapacity){  
    //calculateCapacity()獲取添加元素后所需的最小數組容量
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//獲取添加元素后所需的最小數組容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    //調用的無參構造初始化arraylist,且是第一次添加元素,返回10,即設置‘添加元素后所需的最小數組容量’為10:minCapacity=1,elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA,DEFAULT_CAPACITY為10
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    //其他情況:調用有參構造初始化arraylist且第一次添加元素或非第一次添加元素,則‘添加元素后所需的最小數組容量’為添加元素后實際元素個數
    return minCapacity;
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;//用於快速失敗機制
    //當前數組長度=elementData.length < minCapacity='添加元素后所需的最小數組容量',則需要擴容
    //(1)無參構造第一次添加元素時:10>0,需擴容,傳入10;(2)無參構造添加元素后所需的最小數組容量為11時:11>10,需擴容,傳入11;...
    if (minCapacity - elementData.length > 0) 
        grow(minCapacity);
}
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1); //擴容1.5倍:oldCapacity>>1即oldCapacity/2=0.5*oldCapacity
    if (newCapacity - minCapacity < 0)
    //無參構造第一次添加元素時會走這一步:oldCapacity=newCapacity=0,minCapacity=10
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        //擴容后新容量newCapacity>MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8時
        newCapacity = hugeCapacity(minCapacity);
    //(1)無參構造第一次添加元素時elementData為空數組,newCapacity=10
    elementData = Arrays.copyOf(elementData, newCapacity);
}

OK,如果文章哪里有錯誤或不足,歡迎各位留言。
創作不易,各位的「三連」是二少創作的最大動力!我們下期見!


免責聲明!

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



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