ArrayList源碼淺析

幾個重要接口

首先看方法聲明:java

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

RandomAccess:數組

public interface RandomAccess {
}

RandomAccess接口都是給 List所使用的,用來代表其支持快速(一般是固定時間)隨機訪問,爲其提供良好的性能。實際經驗證實,若是是下列狀況,則 List 實現應該實現此接口,即對於典型的類實例而言,此循環:多線程

for (int i=0, n=list.size(); i < n; i++)
         list.get(i);

的運行速度要快於如下循環:併發

for (Iterator i=list.iterator(); i.hasNext(); )
         i.next();

Cloneable:app

public interface Cloneable {
}

實現了此接口的類就能夠經過重寫 Object.clone()方法來定製對其進行復制的細節,若是在沒有實現 Cloneable 接口的實例上調用 Objectclone 方法,則會致使拋出 CloneNotSupportedException 異常。dom

兩個變量

/**
* ArrayList中元素存儲的地方,數組的長度就是它的容量
*/
private transient Object[] elementData;

/**
 *ArrayList所包含的元素的大小 
*/
private int size;

構造方法

提供了3種構造方法:工具

public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }
    
public ArrayList() {
        this(10);
    }    
    
public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        size = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    }

能夠看到:第一種方法須要一個默認的容量大小,第二個是默認的構造方法,會默認建立一個容量爲10的 ArrayList,第三個則傳給一個 Collection,注意,無論 Collection裏面是什麼類型,最後放進 ArrayList都會上轉爲 Object性能

重要方法

add

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

add方法中使用了 ensureCapacityInternal來控制容量:測試

private void ensureCapacityInternal(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

其中 modCount是用來記錄 list被結構修改的次數,所謂結構上的修改是 指任何添加或刪除一個或多個元素的操做,或者顯式調整底層數組的大小;僅僅設置元素的值不是結構上的修改;在上面的方法中,若是 minCapacity 大於現有數組長度,則執行 grow方法:this

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        /*newCapacity 擴展爲舊容量的1.5倍左右*/
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        /*若是這時新容量還小於minCapacity,則新容量爲minCapacity*/
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        /*新容量大於 Integer.MAX_VALUE - 8*/    
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 最後將原數組放進新數組,改變長度
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

若是 newCapacity大於 Integer.MAX_VALUE - 8,則 newCapacityInteger.MAX_VALUE,這也是可以擴充的最大容量

再來看第二種 add方法:

public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

這種方法是在指定位置插入元素,主要使用了 System.arraycopy()方法將 index以後的元素向後移動一個位置,將 index位空出來放入新元素

clear

clear方法比較簡單,可是注意調用 clear也會使 modCount加1:

public void clear() {
        modCount++;

        // Let gc do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

clone

public Object clone() {
        try {
            @SuppressWarnings("unchecked")
            ArrayList<E> v = (ArrayList<E>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError();
        }
    }

clone方法只能進行淺複製,並不複製元素自己

remove

public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

remove方法中,若是要移除的元素爲 null,則刪除數組中第一個爲 null的元素。若是數組中有超過一個匹配的元素,僅移除第一個

toArray

public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }

這個方法被不少方法使用,它調用了 Arrays工具類中的方法 copyOf

public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

最終調用了方法:

public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)

它的參數列表以下:

src - 源數組。
srcPos - 源數組中的起始位置。
dest - 目標數組。
destPos - 目標數據中的起始位置。
length - 要複製的數組元素的數量。

從指定源數組中複製一個數組,複製從指定的位置開始,到目標數組的指定位置結束。這個方法是一個 native方法,而且經測試,若是是引用數組類型,它不會真正複製對象,只是複製引用(淺複製)

trimToSize

將此 ArrayList 實例的容量調整爲列表的當前大小。應用程序能夠使用此操做來最小化 ArrayList 實例的存儲量。

public void trimToSize() {
        modCount++;
        int oldCapacity = elementData.length;
        if (size < oldCapacity) {
            elementData = Arrays.copyOf(elementData, size);
        }
    }

由此可知:在 ArrayList容量肯定下來之後,能夠調用這個方法最小化存儲空間

Fast-Fail快速失敗機制

此類的 iteratorlistIterator 方法返回的迭代器是快速失敗的:在建立迭代器以後,除了經過迭代器自身的 removeadd 方法從結構上對列表進行修改,不然在任什麼時候間以任何方式對列表進行修改,迭代器都會拋出 ConcurrentModificationException。所以,面對併發的修改,迭代器很快就會徹底失敗,而不是冒着在未來某個不肯定時間發生任意不肯定行爲的風險。
注意,迭代器的快速失敗行爲沒法獲得保證,快速失敗迭代器會盡最大努力拋出 ConcurrentModificationException。迭代器的快速失敗行爲應該僅用於檢測 bug。
前面說到 ArrayList中定義了一個 modCount來記錄對容器進行結構修改的次數,在 addaddAllremoveclearclone方法中都會引發 modCount變化,而在建立迭代器時,會使用局部變量保存當前的 modCount值:

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;
        ...

在進行迭代的過程當中,會先檢查 modCount 有沒有發生變化,以此來斷定是否有外部操做改變了容器:

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

最後,由於 ArrayList是非同步的,所以,在多線程環境下,若是有對容器進行結構修改的操做,則必須使用外部同步。

相關文章
相關標籤/搜索