ArrayList擴容機制

https://blog.csdn.net/u010890358/article/details/80515284 

   ArrayList實現了List接口,它是一個可調整大小的 Object[] 數組可以用來存放各種形式的數據。並提供了包括CRUD在內的多種方法可以對數據進行操作但是它不是線程安全的,外ArrayList按照插入的順序來存放數據。

ArrayList的主要成員變量:

private static final int DEFAULT_CAPACITY = 10;//數組默認初始容量
 
private static final Object[] EMPTY_ELEMENTDATA = {};//定義一個空的數組實例以供其他需要用到空數組的地方調用 
 
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//定義一個空數組,跟前面的區別就是這個空數組是用來判斷ArrayList第一添加數據的時候要擴容多少。默認的構造器情況下返回這個空數組 
 
transient Object[] elementData;//數據存的地方它的容量就是這個數組的長度,同時只要是使用默認構造器(DEFAULTCAPACITY_EMPTY_ELEMENTDATA )第一次添加數據的時候容量擴容爲DEFAULT_CAPACITY = 10 
 
private int size;//當前數組的長度

ArrayList的構造方法有三種:

  1. 默認的構造函數,
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
 
    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;
        }
    }
 
    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);
        }   

在所有添加數據的操作上面都要需要判斷當前數組容量是否足以容納新的數據,如果不夠的話就需要進行擴容。

//判斷是否需要擴容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            //調用grow方法進行擴容,調用此方法代表已經開始擴容了
            grow(minCapacity);
    }
  1. minCapacity的確定:
    1. 當前數組是由默認構造方法生成的空數組(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),並且第一次添加數據。此時minCapacity等於默認的容量(10);
    2. 當前數組是由自定義初始容量構造方法創建並且指定初始容量爲0,第一次添加數據時,minCapacity=1;
    3. 一般時:minCapacity=size+1;
  2. 擴容發生時:當minCapacity > elementData.length時,就使用 grow(minCapacity) 方法進行擴容;
  3. 擴容大小:
    1. 擴容量(newCapacity)設爲舊容量(oldCapacity)的1.5倍;
    2. 注意:當擴容量(newCapacity)大於ArrayList數組定義的最大值( MAX_ARRAY_SIZE= Integer.MAX_VALUE - 8)時,需要根據minCapacity的大小,將newCapacity重新取值爲:MAX_ARRAY_SIZE、或、Integer.MAX_VALUE;
/**
     * ArrayList擴容的核心方法。
     */
    private void grow(int minCapacity) {
        // oldCapacity爲舊容量,newCapacity爲新容量
        int oldCapacity = elementData.length;
        //將oldCapacity 右移一位,其效果相當於oldCapacity /2,
        //我們知道位運算的速度遠遠快於整除運算,整句運算式的結果就是將新容量更新爲舊容量的1.5倍,
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //然後檢查新容量是否大於最小需要容量,若還是小於最小需要容量,那麼就把最小需要容量當作數組的新容量,
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //再檢查新容量是否超出了ArrayList所定義的最大容量,
        //若超出了,則調用hugeCapacity()來比較minCapacity和 MAX_ARRAY_SIZE,
        //如果minCapacity大於MAX_ARRAY_SIZE,則新容量則爲Interger.MAX_VALUE,否則,新容量大小則爲 MAX_ARRAY_SIZE。
        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);
    }
    //比較minCapacity和 MAX_ARRAY_SIZE
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

 

ArrayList線程不安全:高併發下ArrayList空值(null)問題

https://blog.csdn.net/u012859681/article/details/78206494

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

問題1:add操作時可能會導致elementData數組越界

場景:

  1. size爲9;
  2. 線程A開始進入add方法,這時它獲取到size的值爲9,調用ensureCapacityInternal方法進行容量判斷。
    線程B此時也進入add方法,它獲取到size的值也爲9,也開始調用ensureCapacityInternal方法。
  3. 線程A發現需求大小爲10,而elementData的大小就爲10,可以容納。於是它不進行擴容,返回。
  4. 線程B也發現需求大小爲10,也可以容納,也不進行擴容
  5. 線程A開始進行設置值操作, elementData[size++] = e 操作。此時size變爲10。
  6. 線程B也開始進行設置值操作,它嘗試設置elementData[10] = e,而elementData沒有進行過擴容,它的下標最大爲9。於是此時會報出一個數組越界的異常ArrayIndexOutOfBoundsException.

 

問題2:空值(null)問題

線程a:向arraylist實例中添加元素時,先設置elementData[size] = e;然後size++;可能存在的情況:在沒有執行size++時,就讓出cpu給線程b;

線程b:同樣向arraylist實例添加元素,此時size依然爲舊值,線程b添加的元素回覆蓋線程a添加的元素,然後size++;線程a重新執行後,size++;

這樣就會導致,size增加了2,中間的一個元素爲null

 

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