Enumeration不支持fail-fast机制,遍历时即使集合被并发修改也不会报错;Iterator支持fail-fast,通过校验modCount在检测到结构性修改时立即抛出ConcurrentModificationException。

Java 中 fail-fast 机制在 Hashtable 的 Enumeration 和 Iterator 上表现截然不同:前者不触发 fail-fast,后者会严格检测并发修改并抛出 ConcurrentModificationException。
Enumeration 不具备 fail-fast 能力
Enumeration 是 JDK 1.0 的遗留接口,设计初衷就是为 Vector、Hashtable 等早期线程安全集合提供只读遍历能力。它本身没有维护任何“修改计数器”(modCount),也不检查集合结构是否被改动:
- 即使遍历过程中另一个线程调用
put()或remove()修改了Hashtable,Enumeration仍会继续执行,不会中断或报错; - 它的行为是“尽力而为”,结果可能不一致(比如漏掉新插入的条目、重复访问已删除条目),但不会崩溃;
-
Hashtable内部对keys()、elements()返回的Enumeration是同步的,但这种同步仅保证单次调用安全,不提供遍历过程的一致性保障。
Iterator 显式支持 fail-fast
Hashtable 的 keySet().iterator() 或 entrySet().iterator() 返回的是基于 Enumeration 实现的 Iterator,但它额外封装了 fail-fast 逻辑:
- 内部会记录
Hashtable的modCount(结构修改计数器),每次调用next()前都会校验当前值是否匹配; - 一旦发现不一致(例如其他线程执行了
put、remove、clear等结构性操作),立刻抛出ConcurrentModificationException; - 注意:
Iterator.remove()是唯一被允许的修改方式,它会同步更新modCount,因此不会触发异常。
为什么 Iterator 更慢?
性能差异源于职责不同:
立即学习“Java免费学习笔记(深入)”;
-
Enumeration是轻量级纯遍历接口,只有hasMoreElements()和nextElement(),无额外校验; -
Iterator在Hashtable中实际委托给Enumeration执行底层遍历,但每次hasNext()和next()都要多做一次modCount比较; - 实测中
Enumeration通常比Iterator快约 30%–50%,尤其在大数据量下更明显——代价换来了线程协作时的可预测性。
实际开发建议
除非维护非常老的代码或对接遗留 API,否则应优先使用 Iterator:
- 单线程场景下,fail-fast 能快速暴露逻辑错误(如边遍历边删元素却没用
it.remove()); - 多线程场景中,
Enumeration的“静默失败”反而更危险,容易引发难以复现的数据错乱; - 若需高性能且确定无并发修改,可考虑
ConcurrentHashMap配合其弱一致性迭代器——它不抛异常,但明确承诺不抛ConcurrentModificationException,且行为可预期。


















