首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 核心源碼精讀:ArrayList 擴容、迭代器快速失效與序列化機制

     編輯:Java 相關知識

定位:非線程安全、動態陣列、List 接口主力實現

ArrayList 以 Object[] elementData 為底層存儲,實現所有 List 操作,允許 null。與 Vector 幾乎方法級同步不同,ArrayList 僅在 writeObject/readObject 同步,性能優勢顯著。

核心字段

private static final long serialVersionUID = 8683452581122892189L;
private transient Object[] elementData;  // transient:不自動序列化
private int size;                        // 邏輯大小

transient 關鍵:陣列持有的是對象引用而非對象本身,直接序列化引用無意義,需手工序列化實際元素。

擴容機制:1.5 倍增長策略

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 確保容量
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);  // 默認 10
    }
    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);  // 1.5 倍
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

關鍵點:

  • 空構造器初始為空陣列 {},首次 add 才分配默認 10 容量(延遲分配)
  • 擴容公式 old + old/2 平衡了內存浪費與擴容頻次
  • Arrays.copyOf 為 native 實現,高效複製

刪除操作:System.arraycopy 實現元素前移

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;  // 清除引用,助力 GC
    return oldValue;
}

System.arraycopy 為 native 方法,同陣列複製時內部使用臨時緩衝,安全高效。

迭代器快速失效機制

modCount 記錄結構性修改次數(add/remove/clear 等均增 1)。

// AbstractList 內部類
private class Itr implements Iterator {
    int cursor, lastRet = -1;
    int expectedModCount = modCount;  // 快照

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

    public E next() {
        checkForComodification();
        int i = cursor;
        if (i >= size) throw new NoSuchElementException();
        cursor = i + 1;
        return elementData(lastRet = i);
    }

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

設計哲學:不加鎖、不複製、以「版本號比對」極低成本檢測並發修改,發現即拋異常——快速失效。

序列化:手工寫入元素實現深度持久化


private void writeObject(ObjectOutputStream s) throws IOException {
    int expectedModCount = modCount;
    s.defaultWriteObject();  // 寫入 size 等非 transient 字段
    s.writeInt(elementData.length);  // 寫入陣列長度(保留容量信息)
    for (int i = 0; i < size; i++)
        s.writeObject(elementData[i]);  // 逐個序列化元素
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
    s.defaultReadObject();
    int arrayLength = s.readInt();
    elementData = new Object[arrayLength];
    for (int i = 0; i < size; i++)
        elementData[i] = s.readObject();
}

亮點:

  • 保留 capacity(陣列長度),反序列化後無需立即擴容
  • 序列化/反序列化期間檢測 modCount 變化,防止併發修改導致數據不一致
  • synchronized 保證序列化過程原子性

性能對比與選型建議

源碼閱讀清單

  1. java.util.ArrayList (JDK 8/11/17 對比 grow 細節變化)
  2. java.util.AbstractList (理解 modCount、Itr、ListItr 設計)
  3. java.util.Arrays (copyOf 實現)
  4. java.lang.System (arraycopy native 簽名)

閱讀集合框架源碼,不僅是理解「怎麼實現」,更是學習「為何這樣設計」——延遲初始化、1.5倍擴容、快速失效、手工序列化,每一處都是對性能、記憶體、線程安全權衡的經典答案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
場景推薦理由
單線程/線程封閉ArrayList零同步開銷,擴容策略優秀
多線程讀多寫少CopyOnWriteArrayList讀無鎖,寫複製,適合觀察者模式
多線程讀寫均衡Collections.synchronizedList / Vector方法級鎖,簡單但擴展性差
高併發隊列ConcurrentLinkedQueue無鎖 CAS,吞吐量最高