不能边遍历边用集合的remove()方法,因为会触发ConcurrentModificationException;正确做法是通过Iterator.remove()安全删除,且仅可在next()后调用一次。

为什么不能边遍历边用集合的 remove() 方法
直接调用 ArrayList.remove() 或 HashSet.remove() 在 for-each 循环中会触发 ConcurrentModificationException。这是因为迭代器内部维护了 modCount 计数器,而集合自身的修改方法(如 remove()、add())会更新它,但迭代器没被告知这次变更。
正确做法是只通过迭代器自己的 remove() 方法删元素——它会同步更新预期的修改次数:
Iteratorit = list.iterator(); while (it.hasNext()) { String s = it.next(); if (s.startsWith("a")) { it.remove(); // ✅ 安全删除 } }
-
it.remove()只能在调用过next()之后立即调用一次,否则抛IllegalStateException - 不支持
add()或其他结构性修改;想添加元素得用ListIterator - 对
CopyOnWriteArrayList等线程安全集合,普通remove()不报错,但语义不同(操作的是快照)
如何用 for-each 隐式使用 Iterator
Java 的 for-each 语法本质就是编译器帮你展开成 Iterator 调用,所以它天然受限于迭代器协议:
for (String s : list) {
System.out.println(s);
// ❌ 编译不过:list.remove(s);
// ❌ 运行时报错:if (s.equals("x")) list.remove(s);
}
- for-each 中无法访问底层
Iterator实例,因此无法调用remove() - 若需过滤或条件删除,必须显式声明
Iterator变量 - for-each 对
null元素友好,但若集合本身为null,会直接抛NullPointerException
Iterator 和 ListIterator 的关键区别
ListIterator 是 Iterator 的子接口,仅适用于 List 实现,提供双向遍历和插入能力:
立即学习“Java免费学习笔记(深入)”;
-
Iterator只能单向(next()),且无索引信息;ListIterator支持previous()、hasPrevious()、nextIndex()、previousIndex() -
ListIterator允许在遍历时用add()插入新元素(插入位置在next()返回元素之前) -
ListIterator的set()可替换上一次next()或previous()返回的元素(类似“就地更新”) - 获取方式不同:
list.listIterator()或list.listIterator(3)(从索引 3 开始)
遍历 Map 时该用哪个 Iterator
Map 本身不实现 Iterable,所以不能直接 for-each;但它的三个视图(keySet()、values()、entrySet())都返回可迭代对象:
// 推荐:遍历 entrySet,避免重复查 map for (Map.Entryentry : map.entrySet()) { System.out.println(entry.getKey() + "=" + entry.getValue()); } // 若需删除,必须用迭代器 Iterator > it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry e = it.next(); if (e.getValue() < 0) { it.remove(); // ✅ 删除键值对 } }
-
keySet()和values()的Iterator也支持remove(),但values()的删除行为取决于具体实现(如HashMap.values()的remove()会删对应键值对) - 不要用
map.keySet().iterator()然后手动map.remove(key),这又会触发ConcurrentModificationException -
LinkedHashMap的迭代顺序是插入/访问顺序,TreeMap是按键排序,这点由视图继承
remove()。










