1. 程式人生 > >java集合之----ArrayList原始碼分析(基於jdk1.8)

java集合之----ArrayList原始碼分析(基於jdk1.8)

一、ArrayList

1、ArrayList是什麼:

ArrayList就是動態陣列,用MSDN中的說法,就是Array的複雜版本,它提供了動態的增加和減少元素,實現了ICollection和IList介面,靈活的設定陣列的大小等好處,實現了Randomaccess介面,支援快速隨機訪問

//實現RandomAccess介面
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.
io.Serializable

2、初始化

ArrayList有三種初始化方法:
(1)預設建構函式,使用初始容量10構造一個空列表(無引數構造)

    private static final int DEFAULT_CAPACITY = 10;//預設初始容量10
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
    //預設建構函式  
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA
; }

以無引數構造方法建立 ArrayList 時,實際上初始化賦值的是一個空陣列。當真正對陣列進行新增元素操作時,才真正分配容量。即向陣列中新增第一個元素時,陣列容量擴為10
(2)帶初始容量引數的建構函式。(使用者自己指定容量)

public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            //建立initialCapacity大小的陣列
            this.elementData = new Object[initialCapacity];
        }
else if (initialCapacity == 0) { //建立空陣列 this.elementData = EMPTY_ELEMENTDATA; } else {//初始容量小於0,丟擲異常 throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } }

(3)構造包含指定collection元素的列表,這些元素利用該集合的迭代器按順序返回

public ArrayList(Collection<? extends E> c) {//如果指定的集合為null,throws NullPointerException。
        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;
        }
    }

3、擴容

ArrayList每次新增元素時使用 ensureCapacityInternal() 方法來保證容量足夠,如果不夠時,需要使用 grow() 方法進行擴容,擴容之後的容量變為原來的1.5倍。

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 判斷容量是否足夠
    elementData[size++] = e;//將指定元素新增至陣列
    return true;
}

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

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);//呼叫grow方法進行擴容,呼叫此方法代表已經開始擴容了
}

private void grow(int minCapacity) {
     // oldCapacity為舊容量,newCapacity為新容量
    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);
}

擴容操作需要呼叫 Arrays.copyOf() 把原陣列整個複製到新陣列中,這個操作代價很高,因此最好在建立 ArrayList 物件時就指定大概的容量大小,減少擴容操作的次數。

4、刪除元素

刪除元素需要呼叫 System.arraycopy() 將 index+1 後面的元素都左移一位,該操作的時間複雜度為 O(N),所以 ArrayList 刪除元素的代價是非常高的。

public E remove(int index) {//刪除該列表中指定位置的元素。 將任何後續元素移動到左側(從其索引中減去一個元素)。 
    rangeCheck(index);
    modCount++;
    E oldValue = elementData(index);
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index, numMoved);
    elementData[--size] = null; // clear to let GC do its work
    return oldValue;
}

5、Fail-Fast

modCount 用來記錄 ArrayList 結構發生變化的次數。結構發生變化是指新增或者刪除至少一個元素的所有操作,或者是調整內部陣列的大小,僅僅只是設定元素的值不算結構發生變化。

在進行序列化或者迭代等操作時,需要比較操作前後 modCount 是否改變,如果改變了需要丟擲 ConcurrentModificationException。

private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

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

二、總結

(1)ArrayList實現了RandomAccess介面,支援隨機訪問,ArrayList底層基於陣列實現,陣列天然支援隨機訪問,知道下標直接可以得到其值,無需遍歷。這是LinkedList無法做到的。
(2)ArrayList 實現了Cloneable 介面,即覆蓋了函式 clone(),能被克隆。
(3)ArrayList 實現java.io.Serializable 介面,這意味著ArrayList支援序列化,能通過序列化去傳輸。
(4)ArrayList 中的操作不是執行緒安全的!所以,建議在單執行緒中才使用 ArrayList,而在多執行緒中可以選擇 Vector 或者 CopyOnWriteArrayList。