1. 概述

for循环和Iterator都是Java中遍历集合元素的核心机制。虽然两者都能实现集合遍历,但在语法、功能特性和适用场景上存在显著差异。

本文将通过多个维度深入对比for循环和Iterator,帮助开发者在不同场景下做出最佳选择。我们将使用以下字符串列表作为演示案例:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

2. 正向遍历

2.1. 使用for循环

传统for循环天然支持正向遍历,通过索引从集合起始位置逐步移动到末尾,按顺序处理每个元素:

StringBuilder stringBuilder = new StringBuilder(); 

for (int i = 0; i < names.size(); i++) { 
    stringBuilder.append(names.get(i)); 
} 

assertEquals("AliceBobCharlie", stringBuilder.toString());

2.2. 使用Iterator

Iterator默认仅支持正向遍历。通过hasNext()检查下一个元素是否存在,再调用next()移动到下一个位置:

StringBuilder stringBuilder = new StringBuilder();

Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
    stringBuilder.append(iterator.next());
}

assertEquals("AliceBobCharlie", stringBuilder.toString());

3. 反向遍历

3.1. 使用for循环

通过操作循环变量可实现反向遍历,但不如正向遍历直观:

StringBuilder stringBuilder = new StringBuilder();

for (int i = names.size() - 1; i >= 0; i--) {
    stringBuilder.append(names.get(i));
}
assertEquals("CharlieBobAlice", stringBuilder.toString());

3.2. 使用Iterator

当集合实现List接口时,可通过ListIterator的hasPrevious()previous()方法实现反向遍历:

StringBuilder stringBuilder = new StringBuilder();

ListIterator<String> listIterator = names.listIterator(names.size());
while (listIterator.hasPrevious()) {
    stringBuilder.append(listIterator.previous());
}

assertEquals("CharlieBobAlice", stringBuilder.toString());

4. 元素移除

4.1. 使用for循环

⚠️ 踩坑警告:for循环不支持在遍历过程中安全移除元素。修改集合大小会导致不可预测的行为,通常抛出ConcurrentModificationException或索引错误:

assertThrows(ConcurrentModificationException.class, () -> {
    for (String name : names) {
        names.remove("Bob");
    }
});

4.2. 使用Iterator

安全操作:Iterator通过remove()方法提供安全的元素移除机制。其内部维护游标状态,能精确识别待删除元素,避免并发修改问题:

Iterator<String> iterator = names.iterator();

while (iterator.hasNext()) {
    String name = iterator.next();
    if (name.equals("Bob")) {
        iterator.remove();
    }
}

List<String> expected = Arrays.asList("Alice", "Charlie");
assertIterableEquals(expected, names);

5. 灵活性对比

5.1. 使用for循环

for循环通过索引提供直接元素访问能力,支持灵活的修改和插入操作:

for (int i = 0; i < names.size(); i++) {
    names.set(i, names.get(i).toLowerCase());
}

List<String> expected = Arrays.asList("alice","bob", "charlie");
assertIterableEquals(expected, names);

5.2. 使用Iterator

功能限制:Iterator专注于遍历和移除操作,不支持基于索引的访问。若需修改元素,应考虑使用ListIterator:

// Iterator本身不支持元素修改
// 需使用ListIterator的set()方法

6. 错误倾向性

for循环因依赖索引访问更容易出错:

  • 索引越界导致IndexOutOfBoundException
  • 遍历中修改集合大小引发异常

Iterator通过强制hasNext()检查避免空指针异常,显著降低错误率:

// 安全检查示例
while (iterator.hasNext()) {
    String element = iterator.next(); // 确保元素存在
}

7. 代码可读性

  • for循环:简单遍历场景下语法更简洁直观,索引变量明确指示当前位置
  • Iterator:需调用hasNext()/next()等方法,增加代码复杂度,削弱可读性

8. 选择指南

根据场景特性选择合适方案:

特性 for循环 Iterator
遍历方向 双向(通过索引) 默认单向,ListIterator支持双向
元素移除 ❌ 不安全 ✅ 安全可靠
插入/修改灵活性 ✅ 直接索引访问 ❌ 受限(需ListIterator)
错误倾向 ⚠️ 较高(索引相关) ✅ 较低(强制检查)

选择建议

  • 简单遍历且需索引操作 → for循环
  • 需安全移除元素或通用集合遍历 → Iterator
  • 需反向遍历或修改元素 → ListIterator

9. 总结

for循环和Iterator各有适用场景:

  • for循环适合需要索引操作的简单遍历
  • Iterator在安全移除元素和通用遍历场景更具优势

合理选择遍历方式能提升代码健壮性和可维护性。示例代码可在GitHub仓库获取。


原始标题:Comparison of for Loops and Iterators | Baeldung