不能边遍历边用集合的remove()方法,因为Java集合迭代器采用fail-fast机制,通过modCount检测并发修改;正确做法是仅使用Iterator.remove()。

为什么不能边遍历边用集合的remove()方法
直接调用 ArrayList.remove() 或 HashSet.remove() 在增强 for 循环或手动 while 遍历时会抛出 ConcurrentModificationException。这是因为 Java 集合的迭代器默认是“快速失败”(fail-fast)机制,底层通过 modCount 检查结构是否被意外修改。
正确做法是只通过 Iterator.remove() 删除当前元素——它会同步更新 modCount,避免异常。
- 错误写法:
for (String s : list) { if (s.isEmpty()) list.remove(s); } - 正确写法:
Iterator
it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.isEmpty()) it.remove(); // ✅ 唯一安全的删除方式
}
Iterator与ListIterator的区别在哪
Iterator 是单向遍历接口,只能从头到尾调用 next();ListIterator 是它的子接口,仅适用于 List 实现类(如 ArrayList、LinkedList),支持双向移动和元素替换/插入。
常见误用是试图对 Set 或 Map.keySet() 调用 listIterator(),会抛出 UnsupportedOperationException。
立即学习“Java免费学习笔记(深入)”;
-
Iterator提供:hasNext()、next()、remove() -
ListIterator额外提供:hasPrevious()、previous()、nextIndex()、previousIndex()、set()、add() - 获取方式不同:
list.iterator()vslist.listIterator()(或带索引的list.listIterator(3))
forEachRemaining()适合什么场景
Iterator.forEachRemaining() 是 Java 8 新增的默认方法,接收一个 Consumer,对剩余未遍历元素批量执行操作。它比传统 while 循环更简洁,且内部已处理好异常传播和性能优化。
适用于「只读遍历 + 简单处理」,比如日志打印、状态校验、统计计数等;但不适用于需要提前中断、条件跳过或边遍历边修改的逻辑。
- 简洁写法:
iterator.forEachRemaining(s -> System.out.println("Got: " + s)); - 注意:一旦调用
forEachRemaining(),后续再调用next()会抛出NoSuchElementException—— 它会消耗完所有剩余元素 - 底层其实仍是循环,但省去了显式状态管理,也规避了手写 while 时漏写
hasNext()判断的风险
泛型擦除下如何避免ClassCastException
如果用原始类型声明迭代器(如 Iterator it = list.iterator()),编译期不检查类型,运行时取值可能抛 ClassCastException。这是泛型擦除带来的典型陷阱。
必须始终使用带泛型的 Iterator 形式,并确保集合本身也是参数化类型,否则类型安全无法保障。
- 危险写法:
ArrayList list = new ArrayList();
list.add("a"); list.add(123);
Iterator it = list.iterator();
String s = (String) it.next(); // ✖️ 运行时报错 - 安全写法:
ArrayList
list = new ArrayList<>();
list.add("a");
// list.add(123); // 编译报错,杜绝混入 - IDE 通常会在原始类型迭代器上标黄警告:“Iterator is raw type”——别忽略它
Iterator.remove() 是唯一安全删除方式,或者在多线程环境下误以为加锁就能绕过 fail-fast——其实不行,得换用 CopyOnWriteArrayList 或 ConcurrentHashMap 这类线程安全集合。










