假设我们有以下 ArrayList 作为示例:

import java.util.ArrayList;
import java.util.List;
List<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
传统的 for 循环 (索引遍历)
这是最基础、最直观的遍历方式,通过索引来访问每一个元素。
代码示例:
System.out.println("--- 方法一:传统的 for 循环 ---");
for (int i = 0; i < fruits.size(); i++) {
String fruit = fruits.get(i);
System.out.println(fruit);
}
优点:
- 简单易懂:对于初学者来说非常直观。
- 随机访问:可以在循环体内通过
i的值轻松地访问任意位置的元素,非常适合需要根据索引进行操作的场景(需要知道当前是第几个元素)。
缺点:

- 代码冗长:相比其他方法,需要写更多的代码。
- 性能问题:对于非
RandomAccess接口的实现类(如LinkedList),每次调用get(i)都需要从头开始遍历,效率较低,虽然ArrayList是RandomAccess的,性能很好,但这种写法不够通用。
增强 for 循环 (For-Each 循环)
这是 Java 5 引入的一种语法糖,专门用于遍历集合和数组,代码更简洁。
代码示例:
System.out.println("\n--- 方法二:增强 for 循环 (For-Each) ---");
for (String fruit : fruits) {
System.out.println(fruit);
}
优点:
- 代码简洁:代码非常干净,可读性高。
- 不易出错:不需要关心索引的初始化、边界条件和递增,减少了出错的可能性。
- 性能好:对于
ArrayList,其内部实现和传统的 for 循环性能几乎一样。
缺点:
- 无法获取索引:无法在循环中获取当前元素的索引。
- 无法修改集合:如果在遍历过程中尝试删除或添加元素(除了当前元素),会抛出
ConcurrentModificationException异常,如果需要安全地删除元素,请参考方法五。
适用场景:
- 最常用、最推荐的场景:当你只需要集合中的每一个元素,而不关心其索引时,这是首选方法。
使用 Iterator (迭代器)
Iterator 是专门为遍历集合而设计的接口,它提供了更安全的遍历方式,尤其是在需要修改集合时。
代码示例:
System.out.println("\n--- 方法三:使用 Iterator ---");
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
System.out.println(fruit);
}
优点:
- 安全删除:是唯一可以在遍历过程中安全地删除元素的方法,必须使用
iterator.remove(),而不是list.remove()。 - 通用性强:适用于所有集合类,无论其是否实现了
RandomAccess接口。 - 职责分离:将遍历和集合本身的操作分离开来。
缺点:
- 代码稍显复杂:相比 For-Each 循环,代码量稍多。
适用场景:
- 需要在遍历过程中删除或修改集合元素时。
使用 Java 8+ 的 forEach() 方法 (Lambda 表达式)
这是 Java 8 引入的函数式编程风格,非常现代和简洁。
代码示例:
System.out.println("\n--- 方法四:使用 Java 8+ 的 forEach() ---");
// 1. 使用 Lambda 表达式
fruits.forEach(fruit -> System.out.println(fruit));
// 2. 使用方法引用 (更简洁,如果只是调用一个方法)
fruits.forEach(System.out::println);
优点:
- 非常简洁:一行代码即可完成遍历和操作。
- 函数式风格:符合现代编程趋势,易于与其他 Stream API 操作结合。
- 性能良好:内部实现和 For-Each 循环类似。
缺点:
- 需要 Java 8+ 环境。
- 无法获取索引:和 For-Each 一样,无法直接获取索引。
- 无法在遍历中修改集合:同样会抛出
ConcurrentModificationException。
适用场景:
- 现代 Java 项目中,对集合进行简单的、无副作用的操作(如打印、计算等)。
使用 Java 8+ 的 Stream API
Stream API 提供了强大的、声明式的方式来处理集合数据,遍历只是其众多功能中的一个。
代码示例:
System.out.println("\n--- 方法五:使用 Java 8+ 的 Stream API ---");
// 简单遍历
fruits.stream().forEach(System.out::println);
// 结合其他操作,例如过滤
System.out.println("\n--- Stream API 结合过滤 ---");
fruits.stream()
.filter(fruit -> fruit.startsWith("A")) // 过滤出以 "A" 开头的元素
.forEach(System.out::println); // 打印过滤后的结果
优点:
- 功能极其强大:可以方便地进行链式操作,如过滤、排序、映射、聚合等。
- 并行处理:只需将
stream()换成parallelStream(),即可轻松实现并行计算,利用多核 CPU 提升性能。 - 代码清晰:声明式代码,更易于表达业务逻辑。
缺点:
- 性能开销:对于简单的遍历,创建 Stream 对象会带来一些额外的性能开销。
- 需要 Java 8+ 环境。
- 无法在遍历中修改集合。
适用场景:
- 当你不仅需要遍历,还需要对集合进行复杂的转换、过滤、聚合等操作时,这是处理数据流的最佳实践。
如何安全地在遍历中删除元素?
这是一个经典问题,错误的做法会导致 ConcurrentModificationException。
错误示范 (会抛异常):
for (String fruit : fruits) {
if ("Banana".equals(fruit)) {
fruits.remove(fruit); // 错误!
}
}
正确做法 1:使用 Iterator
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
if ("Banana".equals(fruit)) {
iterator.remove(); // 正确!使用迭代器的 remove 方法
}
}
// fruits 现在只包含 "Apple" 和 "Orange"
正确做法 2:使用 Java 8+ 的 removeIf() (推荐) 这是最简洁、最现代的方法。
fruits.removeIf(fruit -> "Orange".equals(fruit)); // fruits 现在只包含 "Apple"
总结与选择建议
| 方法 | 优点 | 缺点 | 最佳适用场景 |
|---|---|---|---|
| 传统 for 循环 | 直观,可获取索引 | 代码冗长,通用性差 | 需要操作索引或随机访问元素时。 |
| 增强 for 循环 | 简洁,可读性高,性能好 | 无法获取索引,遍历时不能修改集合 | 最常用,仅需遍历元素,不关心索引。 |
| Iterator | 安全删除,通用性强 | 代码稍复杂 | 需要在遍历过程中安全地删除元素时。 |
| forEach() | 极其简洁,函数式风格 | 无法获取索引,遍历时不能修改集合 | Java 8+,对集合进行简单、无副作用的操作。 |
| Stream API | 功能强大,支持并行,声明式 | 简单遍历有开销,遍历时不能修改集合 | 需要对集合进行复杂的链式操作(过滤、排序等)。 |
一句话建议:
- 如果只是遍历:优先使用 增强 for 循环 或 Java 8+ 的
forEach()。 - 如果需要删除元素:使用 Iterator 或 Java 8+ 的
removeIf()。 - 如果需要进行复杂的数据处理:使用 Stream API。
