ArrayList源碼分析整理

概述

ArrayList繼承於AbstractList,並實現了Serializable,Cloneable。Iterable,Collection,List,RandomAccess這些接口。

  • Seriallizable ,序列化接口,支持序列化,可通過序列化傳輸
  • Cloneable,能被克隆
  • Iterable,可以被迭代器遍歷
  • Collection,擁有集合操作的所有方法
  • 實現了List接口,擁有增刪改
  • 實現了RandomAccess,支持快速隨機訪問

特點:

  • 底層是一個動態擴容的數組結構,初始容量爲10,擴容時增加1.5倍容量(addAll可能有例外)
  • 允許存放重複數據,存儲順序按照元素添加順序,允許存在null
  • 底層使用Arrays.copyOf函數進行擴容,擴容產生新的數組、對數組內容的深拷貝會耗費性能
  • ArrayList不是一個線程安全的集合,若涉及線程安全,可使用CopyOnWriteArrayList或Collections.synchronizedList()

屬性

	//序列化ID
    private static final long serialVersionUID = 8683452581122892189L;

    //默認容量
    private static final int DEFAULT_CAPACITY = 10;

    //默認空數組
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //無參調用時使用該數組
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //存儲ArrayList元素的數組
    transient Object[] elementData; // non-private to simplify nested class access

    //元素的個數
    private int size;

構造方法

    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
  • 第一個,指定大小的構造方法,如果傳入爲0,直接使用EMPTY_ELEMENTDATA,否則new一個
  • 第二個,空無參構造,將默認數組賦值給他
  • 第三個,參數爲集合類,首先直接利用Collection.toArray()方法得到一個對象數組,並賦值給elementData。

不管調用哪個方法都會初始化內部elementData 。

add系列

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

執行ensureCapacityInternal(size + 1)確保內部容量

private void ensureCapacityInternal(int minCapacity) {
    // 如果創建 ArrayList 時候,使用的無參的構造方法,那麼就取默認容量 10 和最小需要的容量(當前 size + 1 )中大的一個確定需要的容量。
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

若使用的無參的構造方法,那麼就取默認容量 10 和 size + 1 中大的一個確定需要的容量。

第一次執行 ensureCapacityInternal 的時候,要擴容的容量就是 DEFAULT_CAPACITY = 10。

private void ensureExplicitCapacity(int minCapacity) {
    // 修改 +1 
    modCount++;
    // 如果 minCapacity 比當前容量大, 就執行grow 擴容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

private void grow(int minCapacity) {
    // 拿到當前的容量
    int oldCapacity = elementData.length;
    // oldCapacity >> 1 意思就是 oldCapacity/2,所以新容量就是增加 1/2.
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 如果新容量小於,需要最小擴容的容量,以需要最小容量爲準擴容
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 如果新容量大於允許的最大容量,則以 Inerger 的最大值進行擴容
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 使用 Arrays.copyOf 函數進行擴容。
    elementData = Arrays.copyOf(elementData, newCapacity);
}

// 允許的最大容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

  • ensureExplicitCapacity 方法:如果minCapacity 比當前容量大則表明需要擴容。
  • 通過oldCapacity 增加1.5倍 得到 newCapacity ,如果1.5倍後的newCapacity 還不比minCapacity 大,那麼就指定newCapacity 爲minCapacity。
  • 如果新容量大於最大容量,則以Integer最大值進行擴容。
  • 最後調用Arrays.copyOf進行擴容。

可以看到,也並不是每次都是1.5倍擴容的。

擴容完成後:

elementData[size++] = e;
return true;

指定位置添加元素

public void add(int index, E element) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
} 
  • 首先判斷越界
  • 然後確認需不需要擴容
  • 再進行深拷貝

addAll方法

public boolean addAll(int index, Collection<? extends E> c) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(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;
}
  • 基本上和add方法差不多

remove系列

刪除指定下標的元素

public E remove(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    modCount++;
    E oldValue = (E) 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;
} 
  • 先判斷是否越界
  • 把要刪除的元素拿出來
  • 計算要移動的長度,執行System.arraycopy()
  • 最後一個元素置爲null

刪除指定元素

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

private void fastRemove(int index) {
    modCount++;
    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
}

分爲兩種情況

  • 元素爲null,循環查找第一個爲null的元素
  • 元素不爲null,使用o.equals(elementData[index])進行判等,然後進行移動。

set系列

public E set(int index, E element) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    E oldValue = (E) elementData[index];
    elementData[index] = element;
    return oldValue;
}
  • 判斷下標越界情況
  • 修改值

get系列

public E get(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    return (E) elementData[index];
}
  • 先判斷越界,然後返回

clear方法

public void clear() {
    modCount++;
    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;
    size = 0;
}
  • 遍歷所有元素賦值爲null

indexOf 方法

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

分兩種情況

  • null,返回第一個爲null的元素
  • 不爲null,使用o.equals(elementData[i])判等並返回。

lastIndexOf方法

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;
} 
  • 和indexOf 類似,只不過先從後面查起

fail-fast事件

The iterators returned by this class’s iterator() and
listIterator(int) methods are fail-fast
if the list is structurally modified at any time after the iterator is
created, in any way except through the iterator’s own
ListIterator remove() or ListIterator add(Object) methods,
the iterator will throw a ConcurrentModificationException. Thus, in the face of
concurrent modification, the iterator fails quickly and cleanly, rather
than risking arbitrary, non-deterministic behavior at an undetermined
time in the future.

ArrayList 迭代器中的方法都是均具有快速失敗的特性,當遇到併發修改的情況時,迭代器會快速失敗,以避免程序在將來不確定的時間裏出現不確定的行爲。

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;

    public boolean hasNext() {
        return cursor != size;
    }

    @SuppressWarnings("unchecked")
    public E next() {
        // 併發修改檢測,檢測不通過則拋出異常
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
  • 在使用迭代器遍歷時會提前保存一份expectedModCount = modCount,在遍歷時會進行併發修改檢測。
Iterator<String> it = a.iterator();
while (it.hasNext()) {
    String temp = it.next();
    System.out.println("temp: " + temp);
    if("1".equals(temp)){
        a.remove(temp);
    }
}

在開發中我們需要避免上面的操作,正確的做法是使用迭代器提供的刪除方法,而不是直接刪除。

總結

  • ArrayList 底層是一個動態擴容的數組結構,初始容量爲10,每次容量不夠的時候,擴容增加1.5倍容量
  • 增加刪除會改變modCount
  • 增加和刪除都要移動元素比較低效,查找和修改時很高效
  • ArrayList支持null的存儲
  • ArrayList是非線程安全的
  • 可以使用CopyOnWriteArrayList 或者使Collections.synchronizedList() 函數返回一個線程安全的 ArrayList
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章