在Java里Iterator和for循环如何选择_Java遍历方式对比说明

必须用 Iterator 而

不能用 for 循环的情况是遍历中需删除元素,因 Iterator.remove() 是唯一安全机制,其他方式会抛 ConcurrentModificationException。

什么时候必须用 Iterator 而不能用 for 循环

当需要在遍历过程中删除集合元素时,Iterator 是唯一安全的选择。直接用增强 for 循环(for (T t : list))或传统 for 索引循环调用 list.remove(),会触发 ConcurrentModificationException

  • Iterator.remove() 是唯一被设计为在迭代中安全删除元素的机制
  • 增强 for 循环底层就是用 Iterator 实现的,但它隐藏了 remove() 方法,所以无法调用
  • 手动用索引 for (int i = 0; i 删除时,容易漏掉下一个元素(因为索引前移),也需额外处理 i--
Iterator it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if (s.startsWith("tmp")) {
        it.remove(); // 安全删除
    }
}

for-eachIterator 在性能和语义上的实际差异

对大多数集合(如 ArrayListLinkedList),for-each 和显式 Iterator 的性能几乎一致——因为前者编译后就是后者;但语义不同:

  • for-each 更简洁,适合只读遍历,且代码意图明确
  • 显式 Iterator 多了一层对象创建开销(微乎其微),但换来对遍历过程的完全控制(比如提前 break 后还能继续用同一 Iterator?不能,它是一次性的)
  • Iterator 可以配合 try-with-resources(仅限实现了 AutoCloseable 的自定义迭代器,标准 JDK 迭代器不支持)

传统索引 for 循环该用在哪些场景

不是所有遍历都适合抽象成“逐个访问”,当逻辑依赖索引本身时,传统 for 不可替代:

  • 需要同时访问当前元素和相邻元素(如比较 list.get(i)list.get(i+1)
  • 按步长遍历(i += 2)、反向遍历(i = list.size()-1; i >= 0; i--
  • 批量操作:从索引 5 开始截取后半段,或复制某段区间到新数组
  • ArrayList 随机访问快,但对 LinkedList 调用 get(i) 是 O(n) —— 此时强行用索引 for 会严重拖慢性能

容易被忽略的兼容性与泛型细节

Java 5 引入泛型后,Iteratorfor-each 都要求集合声明了正确类型;但绕过泛型(原始类型)会导致运行时问题:

  • 用原始 List 声明 + for-each,编译通过但可能抛 ClassCastException
  • Iteratornext() 返回 Object,若没加泛型,需手动强转,失去编译期检查
  • 某些老框架返回 Enumeration(如 request.getParameterNames()),不能直接用于 for-each,必须用 while (e.hasMoreElements())

真正麻烦的不是选哪个语法,而是理解「谁拥有遍历权」——是集合自己控制(for-each)、你手动掌控(Iterator),还是你要拿捏下标(索引 for)。选错往往不是性能问题,而是删错元素、跳过数据、或在 LinkedList 上写出 O(n²) 遍历。