java.util.ArrayList源碼分析

一、ArrayList 類

這裏分析jdk 1.8,包含一些1.8新特性的源碼

繼承樹如下:  

宏觀上說,ArrayList是基於動態數組實現的,數組具有按索引查找的特性,所以訪問很快,適合經常查詢的數據。

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

二、ArrayList屬性

    //序列號
    private static final long serialVersionUID = 8683452581122892189L;
    //默認容量
    private static final int DEFAULT_CAPACITY = 10;
    //一個空數組,當用戶指定該 ArrayList 容量爲 0 時,返回該空數組
    private static final Object[] EMPTY_ELEMENTDATA = {};
    /*一個空數組實例
    * - 當用戶沒有指定 ArrayList 的容量時(即調用無參構造函數),返回的是該數組==>剛創建一個 ArrayList 時,其內數據量爲 0。
    * - 當用戶第一次添加元素時,該數組將會擴容,變成默認容量爲 10(DEFAULT_CAPACITY) 的一個數組===>通過  ensureCapacityInternal() 實現
    * 它與 EMPTY_ELEMENTDATA 的區別就是:該數組是默認返回的,而後者是在用戶指定容量爲 0 時返回*/
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
     //ArrayList基於數組實現,用該數組保存數據, ArrayList 的容量就是該數組的長度
     //該值爲 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 時,當第一次添加元素進入 ArrayList 中時,數組將擴容值 
    private transient Object[] elementData; //transient:在採用Java默認的序列化機制的時候,被該關鍵字修飾的屬性不會被序列化。
    //ArrayList實際存儲的數據數量
    private int size;

三、ArrayList構造方法

//創建一個初試容量的、空的ArrayList,當初試容量值非法(小於0)時拋出異常
public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }

//無參構造函數:創建一個 空的 ArrayList,此時其內數組緩衝區 elementData = {}, 長度爲 0,當元素第一次被加入時,擴容至默認容量 10
public ArrayList() {
        super();
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

//創建一個包含collection的ArrayList,要放入 ArrayList 中的集合其內元素將會全部添加到新建的 ArrayList 實例中
public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray(); //將集合轉化成Object[]數組
        size = elementData.length;   //把轉化後的Object[]數組長度賦值給當前ArrayList的size,並判斷是否爲0
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        // 這句話意思是:c.toArray 可能不會返回 Object[],可以查看 java 官方編號爲 6260652 的 bug
        if (elementData.getClass() != Object[].class) { // 若 c.toArray() 返回的數組類型不是 Object[],則利用 Arrays.copyOf(); 來構造一個大小爲 size 的 Object[] 數組
            elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // 替換空數組
            this.elementData = EMPTY_ELEMENTDATA;
        }
}

四、ArrayList常用方法

public void trimToSize() 

將數組緩衝區大小調整到實際 ArrayList 存儲元素的大小

    public void trimToSize() {
        // modCount 是 AbstractList 的屬性值:protected transient int modCount = 0;
        // [問] modCount 有什麼用?
        modCount++;
        // 當實際大小 < 數組緩衝區大小時
        // 如調用默認構造函數後,剛添加一個元素,此時 elementData.length = 10,而 size = 1
        // 通過這一步,可以使得空間得到有效利用,而不會出現資源浪費的情況
        if (size < elementData.length) {
            // 注意這裏:這裏的執行順序不是 (elementData = (size == 0) ) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size);
            // 而是:elementData = ((size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size));
            // 這裏是運算符優先級的語法
            // 調整數組緩衝區 elementData,變爲實際存儲大小 Arrays.copyOf(elementData, size)
            //先判斷size是否爲0,如果爲0:實際存儲爲EMPTY_ELEMENTDATA,如果有數據就是Arrays.copyOf(elementData, size)
            elementData = (size == 0)
                    ? EMPTY_ELEMENTDATA
                    : Arrays.copyOf(elementData, size);
        }
    }

 public void ensureCapacity(int minCapacity)

指定 ArrayList 的容量

public void ensureCapacity(int minCapacity) {
        // 最小擴充容量,默認是 10
        //這句就是:判斷是不是空的ArrayList,如果是的最小擴充容量10,否則最小擴充量爲0
        //上面無參構造函數創建後,當元素第一次被加入時,擴容至默認容量 10,就是靠這句代碼
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                ? 0
                : DEFAULT_CAPACITY;
        // 若用戶指定的最小容量 > 最小擴充容量,則以用戶指定的爲準,否則還是 10
        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }
 private void ensureCapacityInternal(int minCapacity):明確 ArrayList 的容量,提供給本類使用的方法
private void ensureExplicitCapacity(int minCapacity):明確 ArrayList 的容量,用於內部優化,保證空間資源不被浪費:尤其在 add() 方法添加時起效
private void grow(int minCapacity):擴容,以確保 ArrayList 至少能存儲 minCapacity 個元素
private static int hugeCapacity(int minCapacity):大容量分配,最大分配 Integer.MAX_VALUE

返回ArrayList實際存儲的元素數量

public int size() {
        return size;
    }

判斷ArrayList是否有元素

public boolean isEmpty() {
        return size == 0;
    }

是否包含o元素

public boolean contains(Object o) {
        // 根據 indexOf() 的值(索引值)來判斷,大於等於 0 就包含
        // 注意:等於 0 的情況不能漏,因爲索引號是從 0 開始計數的
        return indexOf(o) >= 0;
    }

public int indexOf(Object o) 

順序查找,返回元素的最低索引值(最首先出現的索引位置)

public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

 public int lastIndexOf(Object o)

逆序查找,返回元素的最低索引值(最首先出現的索引位置)

public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

 public Object clone()

實現的有Cloneable接口,深度複製:對拷貝出來的 ArrayList 對象的操作,不會影響原來的 ArrayList

public Object clone() {
        try {
            // Object 的克隆方法:會複製本對象及其內所有基本類型成員和 String 類型成員,但不會複製對象成員、引用對象
            ArrayList<?> v = (ArrayList<?>) super.clone();
            // 對需要進行復制的引用變量,進行獨立的拷貝:將存儲的元素移入新的 ArrayList 中
            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(e);
        }
    }

 返回 ArrayList 的 Object 數組

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

public E get(int index)  

獲取指定位置上的元素,從0開始

    public E get(int index) {
        rangeCheck(index);//檢查是否越界
        return elementData(index);
    }
    // 檢查數組是否在界線內
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

 public E set(int index, E element)

設置 index 位置元素的值

public E set(int index, E element) {
        rangeCheck(index);//越界檢查

        E oldValue = elementData(index);//獲取舊數值
        elementData[index] = element;
        return oldValue;
    }

 public boolean add

增加指定的元素到ArrayList的指定位置,默認最後位置

public boolean add(E e) {
        // 確定ArrayList的容量大小---嚴謹
        // 注意:size + 1,保證資源空間不被浪費,
        // ☆☆☆按當前情況,保證要存多少個元素,就只分配多少空間資源
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

//在這個ArrayList中的指定位置插入指定的元素
public void add(int index, E element) {
        rangeCheckForAdd(index);//判斷角標是否越界
        //看上面的,size+1,保證資源空間不浪費,按當前情況,保證要存多少元素,就只分配多少空間資源
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //第一個是要複製的數組,第二個是從要複製的數組的第幾個開始,
        // 第三個是複製到那,四個是複製到的數組第幾個開始,最後一個是複製長度
        System.arraycopy(elementData, index, elementData, index + 1,
                size - index);
        elementData[index] = element;
        size++;
    }

public E remove

移除指定位置的元素 或者指定元素

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;

        return oldValue;
    }

//移除list中指定的第一個元素(符合條件索引最低的)
 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;
    }

更多方法請移步ArrayList源碼分析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章