1. 程式人生 > >Java8 ArrayList源碼分析

Java8 ArrayList源碼分析

alc index cor emp lac length 指定位置 容量 new

java.util.ArrayList是最常用的工具類之一, 它是一個線程不安全的動態數組. 本文將對JDK 1.8.0中ArrayList實現源碼進行簡要分析.

ArrayList底層采用Object[]來存儲, 每次添加元素前都會檢查數組是否有足夠空間容納新的元素.

若數組空間不足則會進行擴容操作, 即創建一個容量更大的數組 並將已有的元素復制到新數組中. 默認情況下新數組的容量是當前容量的1.5倍.

ArrayList使用Arrays.copyOfSystem.arraycopy調用原生(native)方法進行數組復制, 以提高效率.

addAll, removeAll等方法中通常使用c.toArray

方法來獲取容器中所有元素.

ArrayList提供了iterator()listIterator()兩種叠代器, 前者只能向後移動, 而後者可以雙向移動.

iterator()只能刪除上一個訪問的元素, 而listIterator()還可以在遊標位置添加元素.

兩種叠代器都采用fail-fast機制, 即使用modCount記錄結構性改變(添加刪除元素等)的次數, 叠代器在移動前會檢查modCount是否發生改變. 若modCount改變, 則拋出異常中止叠代. 該方法是為了防止其它線程修改容器造成叠代結果不一致.

數據結構與構造器

在介紹構造器之前, 首先介紹一下ArrayList的數據結構:

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

/** 
 * elementData是實際存儲數據的緩沖區
 * 其類型為Object[], 即在內部用Object類存儲元素在取出時進行類型轉換
 * 訪問控制為默認(包內訪問)是為了便於內部類訪問
 * transient關鍵字表示不對該域進行序列化, ArrayList內部重寫了序列化/反序列化方法
 */
transient Object[] elementData; 

// 當前元素數目
private int size;

// 用於表示空實例的數組 
private static
final Object[] EMPTY_ELEMENTDATA = {}; /** * 默認構造器使用的空數組 * 當elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA時, 首次添加元素會使elementData擴容到DEFAULT_CAPACITY */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

接下來可以閱讀ArrayList的幾個構造器:

// 按照指定初始容量進行初始化
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        // 指定容量為0時使用EMPTY_ELEMENTDATA, 而非重新初始化空數組
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        // 不允許負容量
        throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
    }
}

/** 
 * 按照DEFAULT_CAPACITY進行初始化
 * 構造時並未實際建立緩沖區, 在首次添加元素時才會擴容到DEFAULT_CAPACITY
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

/**
 * 根據其它集合對象創建
 * 默認調用Collection.toArray方法, 
 * 若toArray方法返回類型不是Object[], 則利用Arrays.copyOf進行類型轉換
 */
public ArrayList(Collection<? extends E> c) {
    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;
    }
}

Arrays.copyOf用於復制數組, 其封裝了原生(native)方法System.arraycopy, 具有很高的效率.

ArrayList中廣泛使用這兩個方法用於擴容, 插入等操作.

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
    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;
}

添加元素

ArrayList的底層數據結構為數組, 每次向其中添加元素前都會檢查數組容量是否足夠. 若數組已滿則會進行擴容操作.

首先閱讀添加單個元素的方法add(E):

// 向數組末尾添加一個元素, 返回值代表數組是否改變
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    // 對於默認構造器創建的實例, 保證容量不小於DEFAULT_CAPACITY
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    // modCount記錄了實例發生結構性變化的次數, 用於叠代器的fail-fast機制
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

private void grow(int minCapacity) {
    // 計算擴容後新容量, 默認為原容量的1.5倍
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    
    // oldCapacity的1.5倍已經溢出, 所以出現反而變小的情況
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
        
    // 若大於MAX_ARRAY_SIZE則由hugeCapacity取上限
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);

    // 創建新數組並把原有元素移動到新數組中        
    elementData = Arrays.copyOf(elementData, newCapacity);
}
    
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

在制定位置添加元素的add(index, e)方法非常類似:

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

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // elementData類型一定為Object[], 不用Arrays.copyOf進行類型檢查直接調用System.arraycopy即可
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

private void rangeCheckForAdd(int index) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

addAll方法調用c.toArray獲取c中所有元素:

public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}

public boolean addAll(int index, Collection<? extends E> c) {
    rangeCheckForAdd(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;
}

訪問元素

get方法可以訪問指定位置的元素:

public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}

private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

E elementData(int index) {
    return (E) elementData[index];
}

set方法用於修改某位置的元素, 未發生結構性改變不會修改modCount:

public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

toArray方法可以將ArrayList中所有元素作為數組返回:

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

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        // Make a new array of a‘s runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

出於類型安全的原因, 建議使用第二個toArray方法:

List<String> list = new ArrayList<>();
// add sth in list
String[] arr = new String[list.size()];
arr = list.toArray(arr);

刪除元素

remove(index)方法用於移除指定位置的元素:

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

使用System.arraycopy將index後面的元素向前移動一位, 覆蓋被刪除的元素.

將最後位置上的元素設為null便於GC進行回收.

remove(obj)方法會移除第一個與obj相同的元素, 相同關系使用obj.equals方法來判斷:

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
}

removeAll(c)方法移除所有包含在容器c中的元素, retainAll(c)方法移除所有未包含在容器c中的元素.

public boolean removeAll(Collection<?> c) {
     Objects.requireNonNull(c);
    return batchRemove(c, false);
}

public boolean retainAll(Collection<?> c) {
    Objects.requireNonNull(c);
    return batchRemove(c, true);
}

實際完成該操作的是batchRemove方法:

private boolean batchRemove(Collection<?> c, boolean complement) {
    final Object[] elementData = this.elementData;
    int r = 0, w = 0;
    boolean modified = false;
    try {
        // 遍歷ArrayList, 使用`c.contains`判斷是否包含
        for (; r < size; r++)
            if (c.contains(elementData[r]) == complement)
                // 將需要保留的元素移動到數組前部
                elementData[w++] = elementData[r];
    } finally {
        // 保持與AbstractCollection的行為一致
        // 即使c.contains拋出異常仍完成操作
        if (r != size) {
            // r != size 說明發生了contains異常. 
            // 將後部未判斷的部分移動到前面予以保留
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
            w += size - r;
        }
        if (w != size) {
            // w != size 說明有元素被刪除, 執行清理
            for (int i = w; i < size; i++)
                elementData[i] = null;
            modCount += size - w;
            size = w;
            modified = true;
        }
    }
    return modified;
}

叠代器

ArrayList提供了兩個叠代器: iterator()listIterator(). 它們都采用fail-fast機制, 即當叠代器遍歷過程中容器發生結構性改變時, next方法會拋出ConcurrentModificationException異常, 終止叠代.

所謂結構性改變是指modCount發生改變的情況, 所有的add, removey操作以及clear()方法都會修改modCount. fail-fast機制主要為了應對其它線程修改容器導致的不一致問題.

首先閱讀iterator()源碼:

// 獲得叠代器實例
public Iterator<E> iterator() {
    return new Itr();
}

// 叠代器內部實現類
private class Itr implements Iterator<E> {
    int cursor;       // 下一個要返回元素的下標
    int lastRet = -1; // 上一個返回元素的下標, 默認為-1.
    int expectedModCount = modCount;

    // 檢查是否可以繼續遍歷
    public boolean hasNext() {
        return cursor != size;
    }

    // 返回cursor指向的元素, 並將cursor後移一個位置
    @SuppressWarnings("unchecked")
    public E next() {
        // 檢查modCount是否一致
        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];
    }

    // 刪除上一次返回的元素
    public void remove() {
        // 檢查是否返回過元素(成功調用過next方法), 且該元素未被刪除
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            // 刪除元素
            ArrayList.this.remove(lastRet);
            // 修正遊標位置
            cursor = lastRet;
            // 標記上次返回的元素已被刪除, 避免誤刪
            lastRet = -1;
            // 更新expectedModCount, 保證叠代器可以繼續執行
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
    
   final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }

listIterator()可以雙向移動, 除了刪除元素外還可以在遊標位置添加元素:

public ListIterator<E> listIterator() {
    return new ListItr(0);
}

private class ListItr extends Itr implements ListIterator<E> {
    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();
        int i = cursor - 1;
        if (i < 0)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i;
        return (E) elementData[lastRet = i];
    }

    public void set(E e) {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            ArrayList.this.set(lastRet, e);
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    public void add(E e) {
        checkForComodification();

        try {
            int i = cursor;
            ArrayList.this.add(i, e);
            cursor = i + 1;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
}

ArrayList還有兩個內部類用於處理子序列操作:

  • SubList extends AbstractList<E>
  • ArrayListSpliterator<E> implements Spliterator<E>

序列化

ArrayList的序列化會寫入modCount, size和實際的元素. 同樣會檢查modCount是否一致, 以避免並發問題.

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

自定義序列化機制的根本目的在於避免寫入無意義的字段. readObject也按照同樣的策略進行重寫:

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    elementData = EMPTY_ELEMENTDATA;

    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // Read in capacity
    s.readInt(); // ignored

    if (size > 0) {
        // be like clone(), allocate array based upon size not capacity
        ensureCapacityInternal(size);

        Object[] a = elementData;
        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            a[i] = s.readObject();
        }
    }
}

Java8 ArrayList源碼分析