下面我将从最传统的方式开始,逐步介绍到现代 Java 中最推荐的方式,并附上代码示例和优缺点分析。

(图片来源网络,侵删)
准备工作:创建一个示例 List
为了演示,我们先创建一个 List<String> 并添加一些元素,这个 List 将在所有示例中复用。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ListIterationExample {
public static void main(String[] args) {
// 创建一个包含水果名称的 List
List<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elderberry"));
}
}
传统的 for 循环 (索引遍历)
这是最早期、最经典的遍历方式,通过索引来访问 List 中的每一个元素。
代码示例:
List<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elderberry"));
System.out.println("--- 方法一:传统的 for 循环 (索引遍历) ---");
for (int i = 0; i < fruits.size(); i++) {
String fruit = fruits.get(i);
System.out.println("水果: " + fruit);
}
优点:

(图片来源网络,侵删)
- 直观易懂:对于初学者来说,逻辑非常清晰,就是通过下标一个一个地取。
- 随机访问:可以在循环体内部通过
i来随机访问List中的任何元素,fruits.get(0)。 - 兼容性最好:适用于所有版本的 Java 和所有实现了
List接口的集合类。
缺点:
- 代码冗长:需要声明并管理索引变量
i。 - 容易出错:
List为空,fruits.size()为 0,循环不会执行,这是安全的,但如果在循环体内修改了List的大小(例如删除或添加元素),可能会导致IndexOutOfBoundsException或逻辑错误。 - 性能问题:对于
LinkedList这样的非随机访问列表,每次调用get(i)都需要从头部或尾部遍历,时间复杂度为 O(n),整个循环的时间复杂度会达到 O(n²),而对于ArrayList,get(i)的时间复杂度是 O(1),所以整体是 O(n)。
增强型 for 循环 (For-Each 循环)
Java 5 引入了这种语法,也称为 "for-each" 循环,它极大地简化了遍历集合和数组的代码。
代码示例:
List<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elderberry"));
System.out.println("\n--- 方法二:增强型 for 循环 (For-Each) ---");
for (String fruit : fruits) {
System.out.println("水果: " + fruit);
}
优点:

(图片来源网络,侵删)
- 代码简洁:无需关心索引,代码更易读、更优雅。
- 不易出错:自动处理了迭代过程,避免了索引越界的风险。
- 性能良好:底层使用迭代器实现,对于
ArrayList和LinkedList,其时间复杂度都是 O(n),性能稳定。
缺点:
- 无法获取索引:如果你需要知道当前元素是第几个(索引),这种方式不方便。
- 不能安全地修改集合:在遍历过程中,不能直接使用
list.remove()或list.add()方法来修改List,否则会抛出ConcurrentModificationException异常。(见下文关于迭代器删除的说明)
使用 Iterator (迭代器)
Iterator 是 Java 集合框架中专门用于遍历和移除元素的对象,它是“ fail-fast ”机制(快速失败)的基础。
代码示例:
List<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elderberry"));
System.out.println("\n--- 方法三:使用 Iterator ---");
// 1. 获取迭代器
Iterator<String> iterator = fruits.iterator();
// 2. 使用 while 循环遍历
while (iterator.hasNext()) {
String fruit = iterator.next();
System.out.println("水果: " + fruit);
// 示例:如果水果是 "Cherry",则移除它
if ("Cherry".equals(fruit)) {
// 3. 使用迭代器的 remove() 方法安全地移除元素
iterator.remove();
}
}
System.out.println("移除 Cherry 后的 List: " + fruits);
优点:
- 唯一安全的方式:是唯一一种可以在遍历过程中安全地删除元素的方式,必须调用
iterator.remove(),而不是list.remove()。 - 通用性强:适用于所有实现了
Iterable接口的集合,不仅仅是List。 - fail-fast 机制:如果在迭代过程中,除了通过
iterator.remove()之外,任何其他方式修改了集合(例如另一个线程修改了集合,或者代码中直接调用了list.remove()),迭代器会立即检测到并抛出ConcurrentModificationException,帮助开发者尽早发现并发修改的问题。
缺点:
- 代码相对繁琐:需要先获取迭代器,然后使用
while循环,代码比 for-each 循环长。 - 同样无法获取索引。
Java 8+ 的 forEach 和 Lambda 表达式
Java 8 引入了 Stream API 和 Lambda 表达式,提供了函数式风格的遍历方式。
代码示例:
List<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elderberry"));
System.out.println("\n--- 方法四:Java 8+ 的 forEach 和 Lambda ---");
// 使用 Lambda 表达式
fruits.forEach(fruit -> System.out.println("水果: " + fruit));
// 如果方法体只有一行,可以更简洁
// fruits.forEach(System.out::println);
优点:
- 非常简洁:代码行数最少,可读性高,特别是与方法引用(
System.out::println)结合使用时。 - 函数式编程风格:更符合现代编程范式,易于与其他 Stream 操作链式调用。
- 线程安全(外部迭代):
forEach是一种“外部迭代”,由你控制循环过程,因此不存在并发修改异常的问题,如果需要在多线程环境下遍历,可以确保安全。
缺点:
- 无法在遍历中修改集合:
forEach的操作是“只读”的,你不能在 Lambda 表达式中调用remove()方法来修改List。 - 无法提前终止:不像
for循环可以break,forEach会遍历所有元素,如果需要提前终止,可以使用Stream的anyMatch()或allMatch()等操作。 - 需要 Java 8 或更高版本。
Java 8+ 的 Stream API
Stream API 提供了更强大的功能,如过滤、映射、聚合等,遍历只是其众多功能之一。
代码示例:
List<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elderberry"));
System.out.println("\n--- 方法五:Java 8+ 的 Stream API ---");
// 过滤出长度大于 5 的水果,并打印
fruits.stream()
.filter(fruit -> fruit.length() > 5)
.forEach(fruit -> System.out.println("长水果名: " + fruit));
优点:
- 功能极其强大:可以进行复杂的链式操作,如
filter(),map(),sorted(),reduce()等。 - 支持并行处理:只需将
.stream()换成.parallelStream(),就可以轻松实现并行计算,利用多核 CPU 提升性能。 - 代码声明式:你只需要“声明”你想做什么,而不必关心“怎么做”。
缺点:
- 性能开销:对于简单的遍历,创建 Stream 对象会有一定的性能开销。
- 无法修改集合:和
forEach一样,Stream 操作也是只读的。 - 需要 Java 8 或更高版本。
总结与如何选择
| 方法 | 代码示例 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
传统 for 循环 |
for (int i=0; i<list.size(); i++) |
直观,可随机访问,兼容性好 | 冗长,易出错,LinkedList 性能差 |
需要索引或随机访问时;旧项目兼容。 |
增强型 for 循环 |
for (T item : list) |
简洁,易读,不易出错 | 无法获取索引,遍历中不能修改集合 | 日常遍历的首选,绝大多数情况都适用。 |
Iterator |
while (it.hasNext()) { it.next(); } |
唯一安全地删除元素,fail-fast | 代码繁琐,无法获取索引 | 需要在遍历中删除或修改元素时。 |
forEach + Lambda |
list.forEach(item -> ...) |
极其简洁,函数式风格 | 无法修改集合,无法提前终止 | 现代Java项目,代码简洁性要求高。 |
Stream API |
list.stream().filter(...).forEach(...) |
功能强大,支持并行处理 | 性能开销,无法修改集合 | 需要对数据进行复杂操作(过滤、排序等)时。 |
推荐选择:
- 如果只是简单地遍历
List中的每一个元素:使用 增强型for循环,这是最平衡、最通用的选择。 - 如果在遍历过程中需要删除元素:使用
Iterator,并务必调用iterator.remove()。 - 如果使用的是 Java 8+ 且追求代码的简洁和函数式风格:使用
list.forEach()。 - 如果需要对
List进行更复杂的处理(如筛选、转换、聚合):使用StreamAPI。
