| 方式 | 適用場景 | 刪除元素後果 |
|---|---|---|
| 增強 for(for-each) | 唯讀遍歷 | 拋出 ConcurrentModificationException |
| 普通 for (index) | 需索引操作 | 索引錯位導致漏刪或越界 |
| Iterator 迭代器 | 需邊遍歷邊刪除 | 唯一安全方式,呼叫 iterator.remove() |
List<Student> list = new ArrayList<>();
String[] names = {"aaa","bbb","ccc"};
for (Student stu : list) {
for (String name : names) {
if (stu.getName().equals(name)) {
list.remove(stu); // 第一次刪除後,迭代器內部 modCount 改變,下一次 hasNext() 檢測失敗拋出異常
}
}
}
根因:增強 for 底層使用 Iterator,迭代器建立時記錄 expectedModCount = modCount。集合結構變更(remove/add)會修改 modCount,導致 expectedModCount != modCount,觸發快速失效機制。
for (int i = 0; i < list.size(); i++) {
if (list.get(i).getName().equals("target")) {
list.remove(i); // 刪除後後續元素前移,原 i+1 位置元素變為新 i 位置,循環 i++ 導致跳過該元素
}
}
修正:倒序遍歷 for (int i = list.size()-1; i >= 0; i--) 或刪除後 i--。
Iterator<Student> it = list.iterator();
while (it.hasNext()) {
Student stu = it.next();
if (namesContains(stu.getName())) {
it.remove(); // 迭代器內部同步更新 expectedModCount,結構變更合法
}
}
Iterator.remove() 是唯一被 ArrayList、LinkedList 等標準集合支援的安全刪除方法,內部會同步修改 expectedModCount 避免快速失效。
// 過濾保留不符合條件的元素(建立新列表,原列表不變)
List<Student> filtered = list.stream()
.filter(stu -> !namesContains(stu.getName()))
.collect(Collectors.toList());
// 原地刪除(需可變列表)
list.removeIf(stu -> namesContains(stu.getName()));
removeIf 內部同樣使用迭代器遍歷並呼叫 iterator.remove(),線程不安全場景下等價於手寫 Iterator 迴圈,但語意更清晰。
for-each 或 stream()。removeIf;舊版本用 Iterator 手寫迴圈。for (int i = size-1; i >= 0; i--)。CopyOnWriteArrayList 或加鎖同步。