1. 程式人生 > >使用ArrayList時代碼內部發生了什麽(jdk1.7)?

使用ArrayList時代碼內部發生了什麽(jdk1.7)?

optimize try bst fas lock 查看 nal 技術 src

前言

ArrayList(這裏的ArrayList是基於jdk1.7)是在項目中經常使用的集合類,例如我們從數據庫中查詢出一組數據。這篇文章不去剖析它的繼承和實現,只是讓我們知道實例化及增刪改查時它的內部代碼是怎麽實現的。

public class TestList {
	@Test
	public void testArrayList(){
		List<Integer> list = new ArrayList<>();
		
		for (int i = 0; i < 12; i++) {
			list.add(i);
		}
	}
}

  

實例化

List<Integer> list = new ArrayList<>();

  

我們先來看看上面這段實例化ArrayList時,內部發生了什麽。

這調用的是ArrayList的無參構造函數,如下

   
public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
 } 

  

步驟:

  1. 調用父類的構造函數(AbstractList的構造函數)
  2. 賦值elementData(一個空數組{})

這裏把ArrayList內部的屬性說明下,一共有DEFAULT_CAPACITY、EMPTY_ELEMENTDATA、elementData、size。其中DEFAULT_CAPACITY和EMPTY_ELEMENTDATA是被定義為static和final的。而elementData是我們添加元素時存儲的數組,size就是這個數組的大小,DEFAULT_CAPACITY就是數組默認的大小,EMPTY_ELEMENTDATA是一個空數組。

    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
     * DEFAULT_CAPACITY when the first element is added.
     */
    private transient Object[] elementData;

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;

  

實例化部分就到這裏了,其他有參的這裏就不做介紹,需要的可以自己去看看,接下來看看添加元素

添加元素

for (int i = 0; i < 12; i++) {
	list.add(i);
}

  

這裏循環添加了12個元素,是為了查看ArrayList的第一次擴容,接下來看看ArrayList裏面是怎麽實現的

add方法

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

  

add方法中的步驟如下

  1. 調用ensureCapacityInternal,傳入參數(size+1)
  2. elementData數組對象賦值
  3. 返回true

ensureCapacityInternal方法

    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

  

這個方法中傳入的參數名稱為minCapacity,翻譯為中文就是最小容量。

這個方法首先判斷當前數組對象elementData是不是等於空對象EMPTY_ELEMENTDATA。大家可以看下實例化時elementData對象的賦值(如下)。

    public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
    } 

  

如果是,則最小容量minCapacity重新賦值為DEFAULT_CAPACITY和minCapacity中最大的一個數。

然後調用ensureExplicitCapacity方法。斷點內容如下

minCapacity被賦值為DEFAULT_CAPACITY

技術分享圖片

接下來是ensureExplicitCapacity方法

ensureExplicitCapacity方法

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

  

modCount是ArrayList的父類AbstractList的一個屬性,記錄被修改的次數,也是通過這個觸發fail-fast機制的,這裏不過多說明。

下面就是比較傳入的最小容量minCapacity減去elementData數組的長度是否大於0(也就是最小容量minCapacity是否大於elementData數組的長度),是的話調用grow方法。

grow方法

這個方法就是ArrayList的擴容方法,接下來看看方法內部代碼

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        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);
    }

  

由於是第一次調用add方法,所以minCapacity值為ArrayList的默認容量10,如下

技術分享圖片

  1. 接下來定義一個舊容量oldCapacity,值為elementData數組長度
  2. 然後定義一個新容量newCapacity,值為舊容量oldCapacity加上(舊容量oldCapacity右移1位,也就是除以2,不四舍五入)
  3. 接著比較新容量newCapacity和傳入的最小容量minCapacity,誰大就賦值給新容量newCapacity
  4. 如果新容量newCapacity比定義的數組最大容量MAX_ARRAY_SIZE還大的話,就調用hugeCapacity方法,看是否拋出異常還是賦最大值
  5. 調用Arrays.copyOf進行數組擴容

獲取元素

獲取元素有三種方式

  1. 叠代器Iterator遍歷
  2. 通過索引
  3. foreach循環獲取

這裏就介紹下通過叠代器Iterator遍歷吧。

    public Iterator<E> iterator() {
        return new Itr();
    }

  

Itr是ArrayList一個實現Iterator的內部類,通過這個對象來獲取ArrayList中存儲的元素。代碼如下

    /**
     * An optimized version of AbstractList.Itr
     */
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

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

        @SuppressWarnings("unchecked")
        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];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

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

  

總結

當我們實例化ArrayList時(調用無參構造函數),這個對象裏面存儲元素的數組還只是一個空數組。

在第一次調用add方法時(也就是第一次添加元素時),對存儲元素的數組elementData進行第一次擴容,擴容的數組長度為ArrayList內部定義的默認容量10,。

當插入第11個數組元素時,進行第二次擴容,擴容的長度為原先的容量加上(原先容量/2),即原先容量的1.5倍。擴容的長度規則是10>>15>>22>>33...

使用ArrayList時代碼內部發生了什麽(jdk1.7)?