1. 程式人生 > >Java基礎—ArrayList原始碼淺析

Java基礎—ArrayList原始碼淺析

注:以下原始碼均為JDK8的原始碼

一、 核心屬性

  基本屬性如下:

  

  核心的屬性其實是紅框中的兩個:

  

  //從註釋也容易看出,一個是集合元素,一個是集合長度(注意是邏輯長度,即元素的個數,而非陣列長度)

  其中:transient指明序列化時請忽略。

 二、構造器

   一共有3個構造器:

  

    1.構造指定容量的ArrayList

    

    2.預設構造器

    

    //可以看到,預設初始容量為10(基本屬性中的DEFAULT_CAPACITY

    而 DEFAULTCAPACITY_EMPTY_ELEMENTDATA

是一個空陣列,所有預設構造器初始化的都指向它,目的是為了防止我們建立過多的無用的List

    如果建立ArrayList時用的是無參構造器,則第一次插入時會進行一次擴容並且擴到預設陣列大小10

    3.使用collection引數的構造器

    

    //接收一個集合進行構造,按照迭代器返回的順序排列

  更多容器初始化的介紹,參考是清淺池塘知乎專欄:https://zhuanlan.zhihu.com/p/27873515

三、新增元素

  一共有5個(包含2個過載方法)

  

   //由於是基於陣列進行實現的,我們只要抓住它的特點,就能讀懂總體的思路

   1.set(int,E

),使用新元素,替代指定位置舊元素

    

    //比較容易讀懂的一個方法:檢查下標->取得舊元素->替代->返回新元素

    2.add(E)/add(int,E),包含兩個過載方法:在末尾新增元素與在指定位置新增元素

   

   //先檢查容量是否需要擴容(擴容演算法不在這裡展開),再在結尾新增元素(size+1)

   

    //同樣的,涉及下標的都先檢查下標,之後檢查是否需要擴容,之後,由於是陣列,先右移Index+1的元素,再新增

    3.addAll(),兩個addAll方法,與上面類似,一個為在末尾新增,一個在指定位置新增,順序均為迭代器返回的順序,這裡就不展開:

/**
     * Appends all of the elements in the specified collection to the end of
     * this list, in the order that they are returned by the
     * specified collection's Iterator.  The behavior of this operation is
     * undefined if the specified collection is modified while the operation
     * is in progress.  (This implies that the behavior of this call is
     * undefined if the specified collection is this list, and this
     * list is nonempty.)
     *
     * @param c collection containing elements to be added to this list
     * @return <tt>true</tt> if this list changed as a result of the call
     * @throws NullPointerException if the specified collection is null
     */
    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

    /**
     * Inserts all of the elements in the specified collection into this
     * list, starting at the specified position.  Shifts the element
     * currently at that position (if any) and any subsequent elements to
     * the right (increases their indices).  The new elements will appear
     * in the list in the order that they are returned by the
     * specified collection's iterator.
     *
     * @param index index at which to insert the first element from the
     *              specified collection
     * @param c collection containing elements to be added to this list
     * @return <tt>true</tt> if this list changed as a result of the call
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @throws NullPointerException if the specified collection is null
     */
    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

四、獲取元素

  一個我們常見的get方法:

  

  //比較簡單的下標檢查,獲取元素

五、刪除元素

  3個基本的刪除方法:

  

  1.remove(int)/remove(Object),兩個過載方法,分別是按照下標和按照元素刪除:

  

  

  //範圍檢查->修改次數modCount加1->得到將要刪除的元素,被刪除元素後的元素向前進一位,末尾元素置空。

  關於modCount:

 ArrayList也採用了快速失敗的機制,通過記錄modCount引數來實現。在面對併發的修改時,迭代器很快就會完全失敗,而不是冒著在將來某個不確定時間發生任意不確定行為的風險。 

在使用迭代器遍歷的時候,如果使用ArrayList中的remove(int index) remove(Object o) remove(int fromIndex ,int toIndex) add等方法的時候都會修改modCount,在迭代的時候需要保持單執行緒的唯一操作,如果期間進行了插入或者刪除,就會被迭代器檢查獲知,從而出現執行時異常

 

  

  

  //先檢查要刪除的元素在不在,存在則刪除返回true,不存在返回false

  2.removeRange(int,int),按照範圍刪除,實際是將elementData從toIndex位置開始的元素向前移動到fromIndex,然後將toIndex位置之後的元素全部置空順便修改size。這裡就不展開頻率使用不高的範圍刪除方法了:

/**
     * Removes from this list all of the elements whose index is between
     * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
     * Shifts any succeeding elements to the left (reduces their index).
     * This call shortens the list by {@code (toIndex - fromIndex)} elements.
     * (If {@code toIndex==fromIndex}, this operation has no effect.)
     *
     * @throws IndexOutOfBoundsException if {@code fromIndex} or
     *         {@code toIndex} is out of range
     *         ({@code fromIndex < 0 ||
     *          fromIndex >= size() ||
     *          toIndex > size() ||
     *          toIndex < fromIndex})
     */
    protected void removeRange(int fromIndex, int toIndex) {
        modCount++;
        int numMoved = size - toIndex;
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

        // clear to let GC do its work
        int newSize = size - (toIndex-fromIndex);
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        size = newSize;
    }

清空元素也比較容易看懂:

  

  //元素置Null與size置0

  在ArrayList中,底層陣列存/取元素效率非常的高(get/set),時間複雜度是O(1),而查詢,插入和刪除元素效率不高,時間複雜度為O(n)。

並且由原始碼也知道,頻繁地插入刪除會導致底層陣列地複製,這個應當由 LinkedList來彌補它地補足了。