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

Java for-each 循環與 Iterator 迭代器深度解析:原理、陷阱與最佳實踐

     編輯:Java 相關知識

一、Iterable 與 Iterator 體系

interface Iterable<T> {
    Iterator<T> iterator();
}
interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove(); // 選用操作
}
  • 所有 Collection 實作 Iterable,故可用 for-each。
  • iterator() 回傳**新的**迭代器實例,每次遍歷互不干擾。

二、for-each 編譯後等價程式碼

原始碼:

for (String s : list) { System.out.println(s); }

編譯器轉換為(JDK 8+ 簡化版):

Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    System.out.println(s);
}

結論:for-each **本質上就是語法糖**,底層完全依賴 Iterator。

三、經典陷阱:遍歷中修改集合導致 ConcurrentModificationException

錯誤示範 1:用集合自身方法刪除

List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
for (String s : list) {
    if ("B".equals(s)) list.remove(s); // 拋 ConcurrentModificationException
}

錯誤示範 2:用 Iterator 但呼叫錯誤方法

Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if ("B".equals(s)) list.remove(s); // 同樣拋異常,必須用 it.remove()
}

正確做法:呼叫 Iterator.remove()

Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if ("B".equals(s)) it.remove(); // 唯一合法的遍歷中修改方式
}

原理:ArrayList.Itr 內部維護 expectedModCount,若偵測到 modCount != expectedModCount 即拋異常。it.remove() 會同步更新 expectedModCount。

四、自訂類別支援 for-each

只需實作 Iterable<T> 並回傳有效 Iterator:

class MyCollection<T> implements Iterable<T> {
    private final List<T> data = new ArrayList<>();
    public void add(T t) { data.add(t); }
    @Override public Iterator<T> iterator() { return data.iterator(); }
}
// 使用
MyCollection<String> mc = new MyCollection<>();
mc.add("Java"); mc.add("Go");
for (String s : mc) System.out.println(s); // 正常運作

五、ListIterator:雙向遍歷與索引操作

僅 List 實作提供 listIterator():

  • hasPrevious() / previous():反向遍歷。
  • nextIndex() / previousIndex():取得索引。
  • set(E):替換元素。
  • add(E):在游標位置插入。

六、Java 8+ Stream API:函數式替代方案

list.stream().filter(s -> !"B".equals(s)).forEach(System.out::println);
// 或收集新列表
List<String> filtered = list.stream().filter(s -> !"B".equals(s)).collect(Collectors.toList());

優點:不可變風格、避免並發修改異常、可並行 (parallelStream())。

七、效能與選擇指南

場景建議
單純遍歷讀取for-each 最簡潔
遍歷中需刪除/修改顯式 Iterator + remove()/set()
需索引或反向遍歷ListIterator 或 傳統 for (int i=0; i<size; i++)
複雜篩選/轉換/聚合Stream API
高效能數值計算基本型別陣列 + 傳統 for 迴圈

總結

for-each 是 Iterator 的語法糖,**遍歷時修改集合必須使用迭代器自身的 remove()**,否則觸發 ConcurrentModificationException。理解底層 modCount 機制,並善用 ListIterator、Stream 等進階工具,能寫出更安全、更具表達力的集合處理程式碼。

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