Java集合之List

ArrayList


繼承於AbstractList,底層是數組實現的
transient Object[] elementData;
不設置大小則默認大小爲0,當第一次進行add操作時候會擴容,如果容量小於10,默認擴容爲10

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 看ensureCapacityInternal
    elementData[size++] = e;
    return true;
}
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));// 看calculateCapacity
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
        return Math.max(DEFAULT_CAPACITY, minCapacity); // DEFAULT_CAPACITY = 10,所以返回10
    }
    return minCapacity;
}

如果不是初始化擴容,則每次擴容後新數組的長度爲原數組的1.5倍,源碼如下:

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);// 變爲原數組的1.5倍
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

思考:當時用增強for循環的過程中remove一個元素,會報錯麼?
分兩種情況:
一、remove非倒數第二個元素會報異常,ConcurrentModificationException
二、remove倒數第二個元素不會報錯
分析如下:

public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);// 看這個方法
                return true;
            }
    } else {
        ...
    }
    return false;
}

private void fastRemove(int index) {
    modCount++;//這裏modCount進行自增加1(modCount是判斷修改了多少次的計數器)
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // 這裏size自減1
}

由於使用的是增強for循環,遍歷元素是通過Iterator迭代器來實現的,則此時會調用Itr內部類的hasNext方法以及next,我們來看這兩個方法:

public boolean hasNext() {
    return cursor != size;
}

public E next() {
    checkForComodification();// 重點!
    int i = cursor;
    if (i >= size)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    cursor = i + 1;
    return (E) elementData[lastRet = i];
}


在調用next方法時會調用checkForComodification方法

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}


可以看出該方法是檢查修改次數modCount是否等於expectedModCount的,在上面ArrayList的remove方法中,操作後會導致modCount自增1,
而expectedModCount並沒有增加,所以就會拋出ConcurrentModificationException異常。

那麼爲什麼remove倒數第二個的時候不會拋出異常呢?
因爲在執行next方法之前會執行hasNext方法,該方法會判斷cursor(當前元素數組下標+1)是否等於size,不等於說明還有元素則返回true,反之返回false,看上面的fastRemove方法,remove完成後size已經自減1,所以如果remove倒數第二個元素那麼這時cursor是等於size的,因此hasNext方法返回false,就不會執行next方法,也就不會報錯,當刪除的元素非倒數第二個元素時,cursor != size就會返回true,所以會執行next方法,會報錯。

 

Vector


底層數據結構是數組,查詢快,增刪慢。 線程安全,效率低,remove操作同樣具有上述問題,新建該集合時,如果不指定長度,默認是10,每次擴容默認會在原來的基礎上擴大1倍,也可以配置參數指定每次擴容的大小。

LinkedList


底層數據結構是鏈表,查詢慢,增刪快。 線程不安全,效率高,remove操作同樣具有上述問題。

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