在 for-each 或 Iterator 遍歷 List 時直接呼叫 list.remove() 會拋出 ConcurrentModificationException。本文從錯誤示範出發,層層遞進到最佳實踐 Iterator.remove(),並補充 Java 8+ 的函數式寫法。
public void operate(List list) {
for (Iterator it = list.iterator(); it.hasNext(); ) {
String str = it.next();
if ("chengang".equals(str)) {
list.remove(str); // 拋出 ConcurrentModificationException
}
}
}
原因:ArrayList 內部維護 modCount,Iterator 快照 expectedModCount,結構性修改導致不一致檢查失敗。
public void operate(List list) {
List toRemove = new ArrayList<>();
for (String str : list) {
if ("chengang".equals(str)) toRemove.add(str);
}
list.removeAll(toRemove);
}
O(n) 空間、兩次遍歷、程式碼冗長Iterator.remove()public void operate(List list) {
for (Iterator it = list.iterator(); it.hasNext(); ) {
String str = it.next();
if ("chengang".equals(str)) {
it.remove(); // 安全刪除,同步更新 modCount
}
}
}
原理:Iterator.remove() 內部會同步更新 expectedModCount = modCount,避免檢查失敗。
// 1. removeIf(原地修改,返回是否有刪除)
list.removeIf("chengang"::equals);
// 2. Stream 過濾產生新列表(不變原列表)
List filtered = list.stream()
.filter(s -> !"chengang".equals(s))
.collect(Collectors.toList());
| 方法 | 修改原列表 | 適用場景 | Java 版本 |
|---|---|---|---|
Iterator.remove() | 是 | 需在循環中執行額外邏輯 | 1.2+ |
removeIf(Predicate) | 是 | 單純條件刪除,程式碼最少 | 8+ |
Stream.filter() | 否(產生新列表) | 需保留原列表、鏈式操作 | 8+ |
Iterator 而非 for-each(後者隱藏 Iterator 無法呼叫 remove())it.remove(),不可同時呼叫 list.remove()CopyOnWriteArrayList 或加鎖removeIf()「代碼越簡潔,對基礎理解越深」。掌握 Iterator.remove() 與 removeIf(),讓集合刪除既安全又優雅。