1. 程式人生 > >ArrayList原始碼解析[一]

ArrayList原始碼解析[一]

歡迎轉載,轉載煩請註明出處,謝謝。

https://www.cnblogs.com/sx-wuyj/p/11177257.html

自己學習ArrayList原始碼的一些心得記錄..

1.1 ArrayList的體系

  • Iterable : iterable接口裡定義了返回iterator的方法,相當於對iterator的封裝,同時實現了iterable介面的類可以支援for each迴圈;
  • Collction : 集合框架中的根介面,下面有三大子介面.List Set Queue;
  • AbstractCollection: 實現了Collection的一些介面,同時也定義了一些抽象方法交給子類實現.
  • List : List介面定義了一些公用的介面,比如 toArray() size() add()等介面;
  • AbstractList :AbstractList 是一個抽象類,實現了List介面,是隸屬於Java集合框架中的根介面 Collection 的分支

    1.2 ArrayList構造方法.

    /**
     *  有參構造  int型別  集合的大小
     */
    public ArrayList(int initialCapacity) {
        //如果引數大於0,緩衝陣列的大小就為該引數
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        //如果引數等於0,建立一個空的例項陣列
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            //異常為非法容量 + 引數
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

     /**
     * Constructs an empty list with an initial capacity of ten.
     * 無參構造
     */
    public ArrayList() {
        //緩衝陣列為空的預設大小的共享例項
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

     //有參構造   引數為任意類或者E的子類
    //任意類或者E的子類,孫子類等collection都可以構造一個ArrayList出來
    public ArrayList(Collection<? extends E> c) {
        //轉為快取陣列
        elementData = c.toArray();
        //將陣列的長度賦值給size,如果陣列長度不等於0
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            //快取陣列的class不等於object型別陣列的class
            if (elementData.getClass() != Object[].class)
                //copyOf方法:返回一個數組
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            //替換為一個空的陣列
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

以上就是ArrayList的構造方法,註釋是我自己一行一行寫出來的,可能會有所誤解,歡迎批評指正.

1.3 arrayList常用的方法

  • add() : 向集合內部新增元素,這個方法其實是Collection介面中定義的,AbstractCollection抽象類中實現這個方法,ArrayList中重寫了這個方法.如果直接呼叫AbstractCollection中的add方法,那麼只會丟擲一個不支援操作的異常.需要注意一點就是add()一共有兩個,但是其中一個是有boolean的返回值,另一個是void並沒有返回值.

boolean返回值add(),元素新增是為最後一位.

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

首先看一下 ensureCapacityInternal(size + 1)這個方法

    private void ensureCapacityInternal(int minCapacity) {

        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            //max方法為一個三元運算,引數型別為int型別,(a >= b) ? a : b;
            //size+1 大於等於 初始值 返回初始值10
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
又呼叫了ensureExplicitCapacity()方法

    private void ensureExplicitCapacity(int minCapacity) {
            modCount++;
            //需要的大小大於底層陣列的大小
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
繼續呼叫了grow()方法,這個方法很關鍵,先看原始碼

    private void grow(int minCapacity) {
        //陣列長度賦值給oldCapacity
        int oldCapacity = elementData.length;
        //oldCapacity >> 1 相當於 oldCapacity / 2
        //newCapacity 為陣列長度的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //newCapacity小於minCapacity
        if (newCapacity - minCapacity < 0)
            //minCapacity賦值於newCapacity
            newCapacity = minCapacity;
        //newCapacity大於MAX_ARRAY_SIZE
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //通過copyOf方法返回一個數組
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

整體來說 如果size+1小於10,那麼minCapacity就是10,10就是預設大小.如果大於10,那麼minCapacity則為size+1.

minCapacity-lementData.length 如果size+1比底層陣列小,那麼可以繼續向預設陣列中新增元素.如果size+1大於底層陣列,就呼叫grow()進行擴容.

grow()這個方法就是arrayList自動擴容的方法.newCapacity為陣列的1.5倍,先判斷newCapacity是否小於
minCapacity,也就是size+1
如果小於那麼將minCapacity賦值於newCapacity.

再判斷newCapacity是否大於MAX_ARRAY_SIZE,關於MAX_ARRAY_SIZE下面有程式碼.

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

如果大於就呼叫hugeCapacity()方法

    private static int hugeCapacity(int minCapacity) {
        //minCapacity 小於 0 報錯
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        //minCapacity大於MAX_ARRAY_SIZE嗎?
        //大於返回Integer.MAX_VALUE
        //小於返回MAX_ARRAY_SIZE
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

最後將elementData,也就是底層陣列.lenth變為newCapacity.
總結起來說,arrayList的擴容機制其實就修改底層陣列的大小.
繼續回頭add()方法往下看:

elementData[size++] = e;

這個其實很好理解了,e就是你要新增的那個新的元素,size++就是新元素在底層陣列中的位置.

return true;
最後返回一個 true 新增成功

void add(),

    public void add(int index, E element) {
        //校驗index
        rangeCheckForAdd(index);
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

rangeCheckForAdd(index);

 private void rangeCheckForAdd(int index) {
     //如果index大於size或者index小於0 就丟擲異常
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

ensureCapacityInternal(size + 1);

這個在前面有說過,就不在細說,可以去看一下前面add()帶返回值的那個解析.

System.arraycopy(elementData, index, elementData, index + 1,size - index);

呼叫System提供的arraycopy(),這是一個靜態native方法,native這裡不進行說明,可以百度進行檢視.這個方法就是現陣列之間的複製。

    elementData:底層陣列.
    index:陣列要複製的起始位置.
    elementData:複製後的陣列.
    index + 1:陣列放置的起始位置.
    size - index:複製的長度

elementData[index] = element;

將要新增的元素放入指定的位置.

size++

將集合的長度+1.

由於arrayList原始碼較為龐大,暫且先寫一部分,後續會繼續更新.

以上博文個人手寫,如果有錯,希望各位大佬多多體諒,歡迎批評指