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

Java 集合框架 Iterator 迭代器模式源碼深度解析(以 ArrayList 為例)

     編輯:Java 相關知識

迭代器模式:統一遍歷介面

Iterator 模式 將遍歷邏輯從聚合對象中分離,提供統一介面:

  • 客戶端不依賴具體集合內部結構(數組/鏈表/樹/哈希)。
  • 支援多種遍歷方式(正向/反向/過濾/投影)互不干擾。
  • 集合可同時存在多個獨立迭代器。

Java 集合框架核心介面:

public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove(); // 可選操作,UnsupportedOperationException
    default void forEachRemaining(Consumer<? super E> action) { ... } // Java 8
}

ArrayList.Itr 源碼深度剖析 (JDK 17)

private class Itr implements Iterator<E> {
    int cursor;       // 下一個待返回元素的索引
    int lastRet = -1; // 上一次返回元素的索引,-1 表示無
    int expectedModCount = modCount; // 快速失效檢查計數器

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

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

    public void remove() {
        if (lastRet < 0) throw new IllegalStateException();
        checkForComodification();
        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount; // 同步修改計數
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

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

關鍵機制詳解

1. 游標管理

  • cursor:指向下一個將返回元素的索引,初始 0。
  • lastRet:記錄上一次返回元素索引,供 remove() 使用,初始 -1。
  • 調用 next() 時:檢查邊界 → 返回 elementData[cursor] → cursor++、lastRet=cursor。

2. 快速失效機制

  • modCount:AbstractList 定義的 protected transient int modCount,每次結構性修改(add/remove/clear)自增。
  • expectedModCount:迭代器創建時拍攝的快照值。
  • 每次 next()/remove() 前調用 checkForComodification() 比對,不一致拋出 ConcurrentModificationException。
  • 目的:檢測並發修改導致的遍歷不一致,而非保證線程安全(非同步機制)。

3. remove() 語義

  • 僅能移除上一次 next() 返回的元素(lastRet 校驗)。
  • 調用集合自身 remove(lastRet) → 元素後移 → size-- → modCount++。
  • 迭代器同步狀態:cursor = lastRet(指向被刪除位置,下次 next 返回原後續元素)、lastRet = -1、expectedModCount = modCount。
  • 禁止:迭代期間直接調用 list.remove() 或 list.add(),會導致 modCount 變化觸發 CME。

其他集合的迭代器實現對比

集合底層結構迭代器實現要點
LinkedList雙向鏈表私有類 ListItr 繼承 AbstractSequentialList.Itr,持有 Node<E> lastReturned, next 引用,next() 移動 next = next.next
HashMap數組+鏈表/紅黑樹HashIterator 持有 Node<K,V> next, current,遍歷桶數組 + 鏈表節點,expectedModCount 同理
TreeMap紅黑樹PrivateEntryIterator 使用 successor() 中序遍歷獲取下一節點
CopyOnWriteArrayList數組(寫時複製)COWIterator 持有創建時的數組快照 Object[] snapshot,無 modCount、無 CME、弱一致性

正確使用模式

// 1. 標準 for-each(編譯器自動轉為迭代器)
for (String s : list) { ... }

// 2. 顯式迭代器(需 remove 或 訪問索引)
for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
    String s = it.next();
    if (shouldRemove(s)) it.remove(); // 唯一安全刪除方式
}

// 3. Java 8+ forEachRemaining
list.forEachRemaining(s -> { ... }); // 內部使用迭代器,不可 remove

// 4. 並發環境:CopyOnWriteArrayList 或 並發包
List safeList = new CopyOnWriteArrayList<>();
for (String s : safeList) { ... } // 遍歷快照,不拋 CME

常見錯誤與避坑

  1. foreach 中直接 remove:for (String s : list) if (cond) list.remove(s); → CME。
    修正:使用顯式迭代器 it.remove() 或 list.removeIf(Predicate) (Java 8)。
  2. 多線程共享迭代器:Iterator 非線程安全。
    修正:加鎖遍歷、複製快照遍歷、使用並發集合。
  3. 迭代中修改元素屬性 vs 結構:修改元素內部字段(如 user.setName())安全;增刪元素觸發 CME。

總結

Iterator 模式以「游標 + 快照計數器」實現了對動態集合的安全遍歷契約。ArrayList.Itr 作為經典實現,展示了基於數組的索引游標與快速失效檢查的精妙結合。理解其源碼有助於正確使用集合、排查 CME 異常、設計自定義集合類。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved