ArrayList中你分得清楚size與capacity的區別嗎?

JDK版本: openJDK 1.8

問題

在ArrayList中,size跟capacity是一樣的東西嗎?

實驗

在ArrayList中有這麼一個構造函數可以讓我們指定初始的容量(Capacity)。

public ArrayList(int initialCapacity);

然後我們可以利用這個構造函數來創建一個ArrayList並在構造函數裏指定初始容量爲10。接着嘗試在下標爲9的位置插入一個數據。

ArrayList list = new ArrayList<Integer>(10);
int index = 9;
int data = 0;
list.add(index, data);

原本我的想法是,既然我們的初始容量可以容納10個數據,那麼在下標爲9的位置插入一個數據應該是沒有問題的。然而程序卻拋出了一個異常錯誤:

Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 9, Size: 0

根據這個異常錯誤信息,往下標9的位置插入數據超越了數組的界限。

追根究底

這就很奇怪了,明明我們ArrayList的Capacity是10,爲什麼下標9會越界呢?

查看ArrayList的構造函數

爲了尋找原因,我打開了ArrayList的源代碼,首先我們來看一下在ArrayList中可以指定Capacity的構造函數:

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
    	// 我們傳入的參數爲10,因此 ArrayList 會初始化一個大小爲 10 的 Object 數組
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

由於我們傳入的initialCapacity參數爲10,因此ArrayList會初始化一個大小爲10的 Object 數組來儲存我們的數據。

到此爲止看上去都很合理,ArrayList的底層數組實際上是足夠大到可以讓我們往下標9的位置插入數據,那麼問題會是出在哪裏呢?

查看ArrayList的add(index, data)函數

我們的下一步就是要查看ArrayList中可以往指定下標位置插入數據的add(index, data)函數。

public void add(int index, E element) {
	// 這個函數看起來很可疑
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

從源代碼中,我們發現有一個函數看起來很可疑:rangeCheckForAdd(index)

根據名字來看似乎是在確認數組是否越界。ctrl + 左鍵 點擊該函數查看源代碼:

private void rangeCheckForAdd(int index) {
	// 原來是根據 size 來判斷數組是否越界
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

從代碼中可以看出,ArrayList是使用size屬性來確定數組是否越界,並非capacity。

那麼size屬性是從哪裏來的呢?

如果我們再看回add(index, data)函數,就會發現ArrayList在add操作的最後做了size++的操作

讓我們再來看看add(E e)函數的源代碼:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);
    elementData[size++] = e; // 在末尾插入數據之後,size++
    return true;
}

可以看到,add(E e)函數會在數組的末尾插入數據,並進行size++的操作。

也就是說,只有當插入新數據的時候,size纔會往上提升。

同樣的,如果我們檢查 remove(int index) 的源代碼,也會發現它會進行size–的操作。

因此,size表示的是數組中元素的數量,並非數組的容量

總結

在檢查了ArrayList的源代碼之後,我們可以總結出以下結論:

  • 在ArrayList中,size與capacity是不同的概念
  • size指的是ArrayList中元素的數量
  • capacity指的是在ArrayList底層實現中Object數組的大小,也可以理解爲ArrayList的容量
  • ArrayList是使用size來判斷數組是否越界

總結圖

這張總結圖大家可以收藏一下,用作複習。

arraylist

如果大家覺得文章有用的話,請幫忙點個贊,謝謝!

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