ArrayList 源碼分析

1、概述

本文基於 JDK8java

ArrayList 底層經過動態數組的數據結構實現數組

  • 內存須要連續的空間保證
  • 添加操做涉及到數組的動態擴容
  • 添加,刪除都涉及到位置移動操做
  • 隨機查找效率快(下標查找)

ArrayList 的繼承與實現的關係圖以下所示。安全

ArrayList 關係圖

如下說明摘自 JDK 文檔。數據結構

  • Iterable 接口:提供迭代器訪問能力,實現此接口容許對象經過 for-each 循環語句進行遍歷。併發

  • Collection 接口:集合層次結構中的根接口。集合中的一組對象稱爲元素。一些集合容許重複的元素,而另外一些則不容許。有些是有序的,而另外一些是無序的。 JDK 不提供此接口的任何直接實現,它提供了更多特定子接口的實現,例如 Set 和 List 。該接口一般用於傳遞集合並在須要最大通用性的地方使用。dom

  • AbstractCollection 抽象類:此類提供了 Collection 接口的基本實現,以最大程度地減小實現此接口所需的工做。ide

  • List 接口:Collection 接口的子接口,有序集合(也稱爲序列)。用戶經過該接口能夠精確控制列表中每一個元素的插入位置。用戶能夠經過其整數索引(集合中的位置)訪問元素,並在中搜索元素。函數

  • AbstractList 抽象類: 此類提供 List 接口的基本實現以最大程度地減小由「隨機訪問」數據存儲(例如數組) 實現此接口所需的工做。
  • RandomAccess 接口:該標記接口提供支持快速隨機訪問的能力。
  • Cloneable 接口:該標記接口提供實例克隆的的能力。
  • Serializable 接口:該標記接口提供類序列化或反序列化的能力。源碼分析

2、源碼分析

2.1 屬性

/** 
 * 序列號 
 */
private static final long serialVersionUID = 8683452581122892189L;

/** 
 * 默認容量爲 10,經過 new ArrayList() 建立
 */
private static final int DEFAULT_CAPACITY = 10;

/**
 * 空數組,傳入容量爲 0 時使用,經過 new ArrayList(0) 建立
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * 空數組,與 EMPTY_ELEMENTDATA 區分開來,經過 new ArrayList() 建立
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
 * 存儲元素的數組
 * transient 修飾此對象表示不序列化該屬性,由於 ArrayList 具備動態擴容的特性,數組中的元素會有剩餘
 * 經過 writeObject 和 readObject 方法實現序列化和反序列化
 */
transient Object[] elementData; // non-private to simplify nested class access

/**
 * 數組的實際長度
 */
private int size;

/**
 * 能夠分配的最大容量
 * Integer.MAX_VALUE - 8 是由於數組中有虛擬機保留的一些數據
 * 強制分配可能會致使 OutOfMemoryError
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

2.2 構造方法

2.2.1 ArrayList()

無參構造器,經過 new ArrayList() 調用。性能

/**
 * 無參構造器,經過 new ArrayList() 調用
 * 懶初始化,在添加第一個元素時將 elementData 擴容爲 DEFAULT_CAPACITY,減小內存的開銷
 */
public ArrayList() {
    // 將 elementData 初始化爲 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

2.2.2 ArrayList(int initialCapacity)

構造一個具備指定初始容量的集合。

/**
 * 構造一個具備指定初始容量的集合
 * @param  initialCapacity 初始容量
 * @throws IllegalArgumentException 初始容量爲負時拋出異常
 */
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        // 初始化容量大於 0 的話,構造一個容量爲 initialCapacity 的數組
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
         // 初始化容量等於 0 的話,初始化爲空數組 EMPTY_ELEMENTDATA
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
         // 初始化容量小於 0 的話就拋出異常
        throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
    }
}

2.2.3 ArrayList(Collection<? extends E> c)

構造一個包含指定集合元素的集合。

/**
 * 構造一個包含指定集合元素的集合
 * @param c 將其元素放入此列表的集合
 * @throws NullPointerException 集合爲空時拋出
 */
public ArrayList(Collection<? extends E> c) {
    // 將集合轉爲 Object[] 類型數組
    elementData = c.toArray();
    // 將 elementData.length 賦值給 size 並判斷是否爲 0
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        // 由於當子類重寫父類時,能夠修改返回值類型,致使返回的類型可能不爲 Object[]
        if (elementData.getClass() != Object[].class)
            // 利用 Arrays 的 copyOf 函數複製成 Object[] 類型的 elementData
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
         // 傳入集合長度爲 0 的話,初始化爲空數組 EMPTY_ELEMENTDATA
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

2.3 主要方法

2.3.1 add(E e)

添加特定的元素到集合末尾。

/**
 * 添添加特定的元素到集合末尾
 * @param e 添加到集合的元素
 * @return 返回是否插入成功
 */
public boolean add(E e) {
    // 檢查插入一個元素是否須要擴容
    ensureCapacityInternal(size + 1);
    // 將元素插入到最後一位
    elementData[size++] = e;
    return true;
}

/**
 * 確保傳入的最小內部容量
 * @param minCapacity 最小容量
 */
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

/**
 * 計算所須要的最小容量
 * @param elementData 原始數組
 * @param minCapacity 所須要的最小容量
 * @return 擴容後的大小
 */
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    // 若 elementData 爲 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,也就是經過 new ArrayList() 初始化的
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // 此時 minCapacity 應該爲 1,返回初始化的默認容量 10
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

/**
 * 確保明確的須要擴容
 * @param minCapacity 所須要的最小容量
 */
private void ensureExplicitCapacity(int minCapacity) {
    // 修改次數 +1,用於 fail-fast 機制
    modCount++;
    // 防止代碼溢出,當所須要的最小容量大於 elementData 的長度時才進行擴容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

/**
 * 增長容量以確保它至少能夠保存最小容量參數指定數量的元素。
 * @param minCapacity 所須要的最小容量
 */
private void grow(int minCapacity) {
    // 舊容量大小
    int oldCapacity = elementData.length;
    // 新容量 = 舊容量 + 舊容量*0.5,也就是舊容量的 1.5 倍大小
    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:
    // 擴容完畢,win!將新容量複製給 elementData
    elementData = Arrays.copyOf(elementData, newCapacity);
}

/**
 * 由於根據舊容量*1.5分配容量超出最大容量,因此該函數用於計算最大容量分配
 * @param minCapacity
 * @return
 */
private static int hugeCapacity(int minCapacity) {
    // 所須要最小容量小於 0 拋出 OutOfMemoryError
    if (minCapacity < 0)
        throw new OutOfMemoryError();
    // 當所需容量大於 MAX_ARRAY_SIZE 時返回 Integer.MAX_VALUE 不然返回 MAX_ARRAY_SIZE
    return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

2.3.2 add(int index, E element)

將指定的元素插入集合中指定位置。將當前在該位置的元素(若是有的話)和全部後續元素向右移動。

/**
 * 將指定的元素插入集合中指定位置
 * 將當前在該位置的元素(若是有的話)和任何後續元素向右移動
 * @param index 插入元素的位置
 * @param element 插入的元素
 * @throws IndexOutOfBoundsException 數組越界異常
 */
public void add(int index, E element) {
    // 檢查該數組下標是否越界
    rangeCheckForAdd(index);
    // 檢查插入一個元素是否須要擴容
    ensureCapacityInternal(size + 1);
    // 將 elementData 中 index 位置開始的元素
    // 複製到 elementData 中 index + 1 開始的位置,複製長度爲 size-index
    System.arraycopy(elementData, index, elementData, index + 1, size - index);
    // 將該元素添加到指定下標位置
    elementData[index] = element;
    // 數組實際長度 +1
    size++;
}

/**
 * 檢查添加時數組下標是否越界
 * add 和 addAll 使用的 rangeCheck 版本。
 */
private void rangeCheckForAdd(int index) {
    // 下標大於數組實際長度或小於 0 時拋出 IndexOutOfBoundsException
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

/**
 * 構造 IndexOutOfBoundsException 的詳細異常信息
 */
private String outOfBoundsMsg(int index) {
    // 返回越界的下標大小和實際數組長度
    return "Index: "+index+", Size: "+size;
}

2.3.3 addAll(Collection<? extends E> c)

將指定集合中的全部元素追加到集合的末尾。

/**
 * 將指定集合中的全部元素追加到集合的末尾
 *
 * @param c 包含要添加到該集合的元素集合
 * @return 集合是否插入成功
 * @throws NullPointerException 指定集合爲空時拋出
 */
public boolean addAll(Collection<? extends E> c) {
    // 將集合 c 轉爲 Object[] 類型數組
    Object[] a = c.toArray();
    // 插入的集合的長度
    int numNew = a.length;
    // 檢查容量爲 size + numNew 時的擴容
    ensureCapacityInternal(size + numNew);
    // 將集合 a 中的全部元素拷貝到 elementData 的最後
    System.arraycopy(a, 0, elementData, size, numNew);
    // 增長數組的實際長度
    size += numNew;
    // 若是插入的集合不爲空就返回 true,不然返回 false
    return numNew != 0;
}

2.3.4 addAll(int index, Collection<? extends E> c)

從指定位置開始,將指定集合中的全部元素插入此集合。將當前位於該位置的元素(若是有)和全部後續元素右移。

/**
 * 從指定位置開始,將指定集合中的全部元素插入此集合
 * 將當前位於該位置的元素(若是有)和全部後續元素右移)
 *
 * @param index 插入集合的指定索引位置
 * @param c 插入的指定集合
 * @return 集合是否插入成功
 * @throws IndexOutOfBoundsException 插入的下標是否越界
 * @throws NullPointerException 集合爲空拋出異常
 */
public boolean addAll(int index, Collection<? extends E> c) {
    // 檢查插入的指定下標是否越界
    rangeCheckForAdd(index);
    // 將集合 c 轉爲 Object[] 類型數組
    Object[] a = c.toArray();
    // 集合 c 的長度
    int numNew = a.length;
    // 檢查容量爲 size + numNew 時的擴容
    ensureCapacityInternal(size + numNew);

    // 指定位置後數組要移動的長度
    int numMoved = size - index;
    if (numMoved > 0)
        // 將 elementData 中 index 開始的元素複製到 elementData 中 index+numNew 的位置,複製長度爲 numMoved
        System.arraycopy(elementData, index, elementData, index + numNew, numMoved);
    // 將添加的數組 a 的數據複製到 elementData 中 index 開始的位置,複製長度爲 numNew
    System.arraycopy(a, 0, elementData, index, numNew);
    // 增長數組的實際長度
    size += numNew;
    // 若是插入的集合不爲空就返回 true,不然返回 false
    return numNew != 0;
}

2.3.5 remove(int index)

刪除集合中指定位置的元素,將全部後續元素向左移動。

/**
 * 刪除集合中指定位置的元素,將全部後續元素向左移動。
 *
 * @param index 刪除的指定下標
 * @return 返回刪除的元素
 * @throws IndexOutOfBoundsException 刪除的下標越界時拋出
 */
public E remove(int index) {
    // 檢查下標是否越界
    rangeCheck(index);
    // 修改次數 +1
    modCount++;
    // 取出對應下標中要刪除的元素
    E oldValue = elementData(index);

    // 刪除該下標中的元素,後面元素須要移動的長度
    int numMoved = size - index - 1;
    if (numMoved > 0)
        // 若是 numMoved>0,也就是 index 不爲最後一位
        // 將 elementData 中 index+1 位置開始的元素複製到 index 開始的位置,複製長度爲 numMoved
        System.arraycopy(elementData, index+1, elementData, index, numMoved);
    // 將最後一個元素置爲 null,方便 GC 回收,刪除的時候並無縮小容量
    elementData[--size] = null;
    // 返回刪除的元素
    return oldValue;
}

/**
 * 檢查給定的索引是否在範圍內。若是不是,則拋出 IndexOutOfBoundsException
 * 此方法不檢查索引是否爲負數,若是索引爲負數,則拋出 ArrayIndexOutOfBoundsException
 */
private void rangeCheck(int index) {
    // 當該下標大於等於數組實際長度時拋出異常
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

2.3.6 remove(Object o)

從集合刪除第一次出現的指定元素,若是存在,則將其刪除。若是集合不包含元素,則集合保持不變。

/**
 * 從集合刪除第一次出現的指定元素,若是存在,則將其刪除。若是集合不包含元素,則集合保持不變。
 * @param o 須要刪除的元素
 * @return 返回是否刪除成功
 */
public boolean remove(Object o) {
    // 若是要刪除的元素爲 null
    if (o == null) {
        // null 特殊處理,用 == 做比較
        // 遍歷全部 elementData,找到第一個值爲 null 的,快速刪除對應的下標,返回 true
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        // 遍歷全部 elementData,使用 equals 比較兩個對象,找出第一次相等位置的下標快速刪除,返回 true
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    // 沒有對應的元素返回 false
    return false;
}

/**
 * 私有remove 方法,跳過邊界檢查而且不返回刪除的值。
 * 與 remove(int index) 相比,缺乏越界檢查和返回值
 */
private void fastRemove(int index) {
    // 減小越界檢查,提升性能
    // 修改次數 +1
    modCount++;
    // 刪除該下標中的元素,後面元素須要移動的長度
    int numMoved = size - index - 1;
    if (numMoved > 0)
        // 若是 numMoved>0,也就是 index 不爲最後一位
        // 將 elementData 中 index+1 位置開始的元素複製到 index 開始的位置,複製長度爲 numMoved
        System.arraycopy(elementData, index+1, elementData, index, numMoved);
    // 將最後一個元素置爲 null,方便 GC 回收,刪除的時候並無縮小容量
    elementData[--size] = null;
    // 無返回值
}

2.3.7 removeAll(Collection<?> c)

從集合中刪除指定的集合中包含的全部元素。

/**
 * 從集合中刪除指定的集合中包含的全部元素
 * @param c 刪除的集合
 * @return 返回是否刪除
 * @throws ClassCastException 若是該集合的元素的類與指定的集合不兼容拋出
 * @throws NullPointerException 若是此列表包含 null 元素,而且指定的集合不容許使用null元素拋出
 */
public boolean removeAll(Collection<?> c) {
    // 集合 c 不爲空,有空的話拋出 NullPointerException
    Objects.requireNonNull(c);
    // 批量刪除包含集合 c 的元素
    return batchRemove(c, false);
}

/**
 * 批量刪除集合
 * @param c 刪除的集合
 * @param complement 刪除的方式
 * complement == true 表示刪除不包含在集合 c 中的元素,求兩個集合的交集
 * complement == false 表示刪除包含在集合 c 中的元素,求兩個集合的差集
 * @return
 */
private boolean batchRemove(Collection<?> c, boolean complement) {
    final Object[] elementData = this.elementData;
    // r 爲寫指針 w 爲讀指針
    int r = 0, w = 0;
    // 是否修改的標誌
    boolean modified = false;
    try {
        for (; r < size; r++)
            // 當 c 中不包含 elementData[r] 且 complement == false 時,elementData[w++] 存放的保留元素就是 elementData 中除去集合 c 有的元素
            // 當 c 中包含 elementData[r] 且 complement == true 時,elementData[w++] 存放的是保留元素就是 elementData 和 集合 c 的交集
            // 無論哪一種方式,存放的都是對應保留的元素
            if (c.contains(elementData[r]) == complement)
                elementData[w++] = elementData[r];
    } finally {
        // 正常循環結束 r == size,不然 c.contains() 拋出了異常
        if (r != size) {
            // 將 elementData 中 r 位置開始的元素複製到 elementData 中 w 開始的位置,複製長度爲 size-r
            // 也就是將出錯位置 r 開始全部元素移動到已經保留的元素 w 位置以後
            System.arraycopy(elementData, r, elementData, w, size - r);
            // 更新保留的元素個數
            w += size - r;
        }

        // 若是 w == size,表示所有元素保留,沒有修改,返回 false
        if (w != size) {
            // 將未保留的元素置爲 null,方便 GC 回收
            for (int i = w; i < size; i++)
                elementData[i] = null;
            // 修改的次數 +未保留的元素個數
            modCount += size - w;
            // 將保留的元素個數更新爲數組實際長度
            size = w;
            // 標記修改爲功
            modified = true;
        }
    }
    // 修改失敗返回 false
    return modified;
}

2.3.8 retainAll(Collection<?> c)

僅保留此集合中指定集合中包含的元素。換句話說,從集合中刪除全部不包含在指定集合中的元素。

/**
 * 僅保留此集合中指定集合中包含的元素。換句話說,從集合中刪除全部不包含在指定集合中的元素。
 * @param c 須要保留的指定集合
 * @return 返回是否刪除
 * @throws ClassCastException 若是該集合的元素的類與指定的集合不兼容拋出
 * @throws NullPointerException 若是此列表包含 null 元素,而且指定的集合不容許使用null元素拋出
 */
public boolean retainAll(Collection<?> c) {
    // 集合 c 不爲空,有空的話拋出 NullPointerException
    Objects.requireNonNull(c);
    // 批量刪除包含集合 c 的元素,和 removeAll 函數的區別就是 complement 參數
    return batchRemove(c, true);
}

2.3.9 get(int index)

返回集合中指定位置的元素。

/**
 * 返回集合中指定位置的元素
 * @param  index 須要返回元素的下標
 * @return 返回指定位置的元素
 * @throws IndexOutOfBoundsException 下標越界時拋出
 */
public E get(int index) {
    // 檢查下標是否越界
    rangeCheck(index);
    // 返回對應索引的元素
    return elementData(index);
}

/**
 * 返回對應位置的元素
 */
@SuppressWarnings("unchecked")
E elementData(int index) {
    // 取出對應位置的元素並強轉
    return (E) ele\mentData[index];
}

2.3.10 set(int index, E element)

用指定的元素替換集合中指定位置的元素。

/**
 * 用指定的元素替換集合中指定位置的元素
 * @param 替換的位置
 * @param element 替換成的元素
 * @return 返回被替換前的元素
 * @throws IndexOutOfBoundsException 索引下標越界拋出
 */
public E set(int index, E element) {
    // 檢查下標是否越界
    rangeCheck(index);
    // 取出對應下標的元素
    E oldValue = elementData(index);
    // 更新指定位置的元素
    elementData[index] = element;
    // 返回更新前的值
    return oldValue;
}

2.3.11 trimToSize()

將該集合的容量調整爲實際的大小,可使用此操做來最大程度地減小元素的存儲。

/**
 * 將該集合 elementData 的容量調整爲實際的大小,可使用此操做來最大程度地減小元素的存儲。
 */
public void trimToSize() {
    // 修改次數 +1
    modCount++;
    // 若是集合實際長度小於 elementData 的長度
    if (size < elementData.length) {
        // 若是實際長度爲 0,elementData 初始化爲 EMPTY_ELEMENTDATA,不然將 elementData 從新複製爲一個長度爲 size 的數組
        elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size);
    }
}

2.3.12 writeObject(java.io.ObjectOutputStream s)

實現 ArrayList 實例的序列化。

/**
 * 將 ArrayList 實例的狀態保存到流中(即對其進行序列化)。
 * @serialData 寫出支持 ArrayList 實例的數組的長度(int),而後以正確的順序寫出全部元素(Object)。
 */
private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // 定義 expectedModCount 記錄寫出前的 modCount, 防止在序列化期間元素被修改
    int expectedModCount = modCount;
    // 默認的序列化方法(寫入非 transient 和非 static 修飾的屬性,size 屬性會被寫入)
    s.defaultWriteObject();

    // 寫出大小 size
    s.writeInt(size);

    // 按正確的順序寫出全部元素
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

    // 若是在此期間元素個數發生了變化,拋出 ConcurrentModificationException
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}

2.3.13 readObject(java.io.ObjectInputStream s)

從反序列化中重構 ArrayList 實例。

/**
 * 從流中重構 ArrayList 實例(即反序列化)。
 */
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    // 初始化空數組
    elementData = EMPTY_ELEMENTDATA;

    // 默認的反序列化方法(讀入非 transient 和非 static 修飾屬性,size 屬性會被讀入)
    s.defaultReadObject();

    // 讀入集合的長度,能夠忽略,只是爲了和寫出對應
    s.readInt();
    
    if (size > 0) {
        // 計算所須要的最小容量
        int capacity = calculateCapacity(elementData, size);
        // 檢查集合的容量和類型
        SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
        // 根據 size 的大小進行擴容檢查
        ensureCapacityInternal(size);
        // 定義空數組
        Object[] a = elementData;
        // 按順序讀取元素存入數組
        for (int i=0; i<size; i++) {
            a[i] = s.readObject();
        }
    }
}

2.3.14 clear()

刪除集合中全部元素。

/**
 * 刪除集合中全部元素
 */
public void clear() {
    // 修改次數 +1
    modCount++;

    // 給每一個元素賦爲 null,以便讓 GC 回收
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    // 數組實際長度置爲 0
    size = 0;
}

2.3.15 toArray()

以正確的順序(從第一個元素到最後一個元素)返回一個包含此集合中全部元素的數組。

/**
 * 以正確的順序(從第一個元素到最後一個元素)返回一個包含此集合中全部元素的數組。 
 * 此方法充當基於數組的 API 和基於集合的 API 之間的橋樑。
 * @return 返回以適當順序包含此列表中全部元素的數組
 */
public Object[] toArray() {
    // 將集合複製爲數組
    return Arrays.copyOf(elementData, size);
}

2.3.16 contains(Object o)

判斷集合是否包含指定元素。

/*
 * 判斷集合是否包含指定元素。
 */
public boolean contains(Object o) {
    // 經過調用 indexOf 方法返回的下標來判斷是否存在該元素
    return indexOf(o) >= 0;
}

/**
 * 返回指定元素在集合中首次出現的索引,若是集合不包含該元素,則返回-1
 */
public int indexOf(Object o) {
    // 若是指定元素爲 null
    if (o == null) {
        // 從頭遍歷全部元素,null 用 == 判斷,返回第一次 null 出現的下標
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        // 從頭遍歷全部元素,使用 equals 判斷,返回第一次該元素出現的下標
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    // 不存在該元素則返回 -1
    return -1;
}

2.3.17 lastIndexOf(Object o)

返回指定元素在集合中最後一次出現的索引,若是集合不包含該元素,則返回 -1。

/**
 * 返回指定元素在集合中最後一次出現的索引,若是集合不包含該元素,則返回-1
 */
public int lastIndexOf(Object o) {
    // 若是指定元素爲 null
    if (o == null) {
        // 從後往前遍歷全部元素,null 用 == 判斷,返回第一次 null 出現的下標,也就是最後一次出現
        for (int i = size-1; i >= 0; i--)
            if (elementData[i]==null)
                return i;
    } else {
        // 從後往前遍歷全部元素,使用 equals 判斷,返回第一次該元素出現的下標,也就是最後一次出現
        for (int i = size-1; i >= 0; i--)
            if (o.equals(elementData[i]))
                return i;
    }
    // 不存在該元素則返回 -1
    return -1;
}

2.3.18 size()

返回集合中的元素的個數。

/**
 * 返回集合中的元素的個數
 */
public int size() {
    // 直接返回 size
    return size;
}

2.3.19 isEmpty()

判斷集合是否爲空。

/**
 * 判斷集合是否爲空
 */
public boolean isEmpty() {
    // 經過判斷集合實際長度是否爲 0
    return size == 0;
}

2.3.20 subList(int fromIndex, int toIndex)

返回集合中指定的 [fromIndex, toIndex) 位置之間的集合。 若是 fromIndex == toIndex,則返回集合爲空。

/**
 * 返回集合中指定 [fromIndex, toIndex) 位置元素構成的集合
 * 若是 fromIndex == toIndex,返回空集合
 */
public List<E> subList(int fromIndex, int toIndex) {
    // 檢測子集的下標是否越界
    subListRangeCheck(fromIndex, toIndex, size);
    // 經過構造 SubList 返回,SubList 和 ArrayList 引用是同一個對象
    return new SubList(this, 0, fromIndex, toIndex);
}

/*
 * 檢測子集的下標是否越界
 */
static void subListRangeCheck(int fromIndex, int toIndex, int size) {
    // 檢測 fromIndex 是否小於 0 
    if (fromIndex < 0)
        throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
    // 檢測 toIndex 是否大於 size
    if (toIndex > size)
        throw new IndexOutOfBoundsException("toIndex = " + toIndex);
    // 檢測 fromIndex 是否大於 toIndex
    if (fromIndex > toIndex)
        throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                           ") > toIndex(" + toIndex + ")");
}

2.4 迭代器

2.4.1 listIterator(int index) 方法

從集合中的指定位置開始,以適當的順序返回此集合中元素的 list 迭代器。 指定的索引表示首次調用 ListIterator 的 next 將返回的第一個元素。 首次調用 ListIterator 的 previous 將返回具備指定索引減一的元素。ListIterator 迭代器爲 List 特有的迭代器。

/**
 * 從集合中的指定位置開始,以適當的順序返回此集合中元素的 list 迭代器。
 * 返回的 list 迭代器爲 fast-fail
 * @throws IndexOutOfBoundsException
 */
public ListIterator<E> listIterator(int index) {
    if (index < 0 || index > size)
        throw new IndexOutOfBoundsException("Index: "+index);
    // 構建一個從指定索引位置的迭代器
    return new ListItr(index);
}

2.4.2 listIterator() 方法

以正確的順序返回此集合中的全部元素的 list 迭代器。

/**
 * 返回此集合中的全部元素的 list 迭代器
 * 返回的 list 迭代器爲 fast-fail
 */
public ListIterator<E> listIterator() {
    // 構建一個從頭開始的 list 迭代器
    return new ListItr(0);
}

2.4.3 iterator() 方法

以正確的順序返回此集合中的全部元素的迭代器。

/**
 * 以正確的順序返回此集合中的全部元素的迭代器
 * 返回的迭代器爲 fast-fail
 */
public Iterator<E> iterator() {
    return new Itr();
}

2.4.4 Itr 類

Itr 類實現了 Iterator 接口,具備迭代器的基本方法。

private class Itr implements Iterator<E> {
    /**
     * 下一個要返回的元素的索引
     */
    int cursor;
    /**
     * 返回最後一個元素的索引,沒有則返回 -1
     */
    int lastRet = -1;
    /**
     * 構建迭代器對象時記錄 modCount
     */
    int expectedModCount = modCount;

    Itr() {}
    
    /**
     * 判斷是否有下一個元素
     */
    public boolean hasNext() {
        return cursor != size;
    }
    
    /**
     * 獲取下一個元素
     */
    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        /* 省略 */
    }

    @Override
    @SuppressWarnings("unchecked")
    public void forEachRemaining(Consumer<? super E> consumer) {
        /* 省略 */
        checkForComodification();
    }
    
    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();
        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            // 將刪除後的 modCount 賦給 expectedModCount
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    /**
     * 檢測 ArrayList 中的 modCount 和當前迭代器對象的 expectedModCount 是否一致
     */
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

2.4.5 ListItr 類

ListItr 類繼承 Itr 類,實現 ListIterator 接口,在迭代器的基本方法上,擴充了 List 特有的迭代器方法。

private class ListItr extends Itr implements ListIterator<E> {
    /**
     * 初始化 ListItr
     */ 
    ListItr(int index) {
        super();
        cursor = index;
    }
    
    /**
     * 判斷是否有上一個元素
     */
    public boolean hasPrevious() {
        return cursor != 0;
    }
    
    /**
     * 返回下一個元素的下標
     */
    public int nextIndex() {
        return cursor;
    }

    /**
     * 返回上一個元素的下標
     */
    public int previousIndex() {
        return cursor - 1;
    }

    /**
     * 返回上一個元素
     */
    @SuppressWarnings("unchecked")
    public E previous() {
        checkForComodification();
        /* 省略 */
    }

    /**
     * 修改元素
     */
    public void set(E e) {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();
        /* 省略 */
    }

    /**
     * 添加元素
     */
    public void add(E e) {
        checkForComodification();
        try {
            int i = cursor;
            ArrayList.this.add(i, e);
            cursor = i + 1;
            lastRet = -1;
            // 將添加後的 modCount 賦給 expectedModCount
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
}

2.4.6 fail-fast 機制

fail-fast 快速失敗機制是集合類應對併發訪問在對集合進行迭代過程當中,內部對象結構發生變化的一種防禦措施。

ArrayList 類的 iterator() 和 listIterator() 方法返回的迭代器都是 fail-fast 的。 若是列表在建立迭代器以後的任什麼時候間進行結構上的修改,除非經過迭代器本身的 remove() 或 add(Object) 方法,不然迭代器將拋出 ConcurrentModificationException。所以,面對併發修改,迭代器會迅速而乾淨地失敗,而不是在未來的不肯定時間內冒着不肯定行爲的風險。

在迭代過程執行 Iterator 的 remove 或 next 方法時,會經過 checkForComodification 方法來判斷 modCount 是否發生了變化,若是在迭代過程當中執行了 ArrayList 方法的 remove 或 add 等方法會形成 modeCount 改變,此時經過 checkForComodification 方法判斷髮現 expectedModCount != modCount,則拋出 ConcurrentModificationException。所以在迭代過程當中進行刪除操做時,須要調用 Iterator 的 remove 方法,另外 foreach 循環本質上也是迭代器實現的。

3、總結

  • ArrayList 容許存放 null 元素。
  • ArrayList 底層是動態數組,當數組新容量超過原始集合大小時,進行擴容,擴容主要方法爲 grow(int minCapacity),不支持縮容。
  • ArrayList 是線程不安全的。
  • ArrayList 實現 RandomAccess 接口,支持隨機訪問,平均時間複雜度爲 O(1)。
  • ArraList 在增長和刪除元素過程當中,效率低下,平均時間複雜度爲 O(n)。
  • ArrayList 經過 addAll 函數,能夠求兩個集合的並集。
  • ArrayList 經過 removeAll 函數,能夠求兩個集合的差集。
  • ArrayList 經過 retainAll 函數,能夠求兩個集合的交集。
相關文章
相關標籤/搜索