一个示例 ArrayList
我们创建一个 ArrayList 并添加一些元素,以便在下面的示例中使用。

import java.util.ArrayList;
import java.util.List;
public class ArrayListTraversal {
public static void main(String[] args) {
// 创建一个存储 String 类型元素的 ArrayList
List<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
fruits.add("Mango");
}
}
我们开始介绍四种遍历方法。
经典 for 循环(索引遍历)
这是最传统、最基础的遍历方式,通过索引来访问列表中的每一个元素。
代码示例
List<String> fruits = new ArrayList<>();
// ... (添加元素)
System.out.println("--- 方法一:经典 for 循环 ---");
for (int i = 0; i < fruits.size(); i++) {
String fruit = fruits.get(i);
System.out.println(fruit);
}
优点
- 直观易懂:对于初学者来说,这种写法最容易理解。
- 随机访问:可以在循环体内方便地通过索引
i访问任意位置的元素,fruits.get(i + 1)。 - 性能:在早期 Java 版本中,这种方式被认为是性能最高的,因为
ArrayList底层是基于数组实现的,通过索引访问非常快。
缺点
- 代码冗长:需要声明索引变量
i,并编写循环条件i < fruits.size()和递增语句i++,代码不够简洁。 - 不适用于所有 List:如果将
ArrayList换成LinkedList,get(i)操作的性能会非常差(需要遍历链表),此时这种方式就不合适了。
增强 for 循环(For-Each 循环)
这是 Java 5 引入的一种语法糖,专门用于遍历集合和数组,代码更简洁。
代码示例
List<String> fruits = new ArrayList<>();
// ... (添加元素)
System.out.println("\n--- 方法二:增强 for 循环 ---");
for (String fruit : fruits) {
System.out.println(fruit);
}
优点
- 代码简洁:无需关心索引,语法非常清晰,可读性高。
- 类型安全:编译器会检查集合中的元素类型是否与声明的变量类型(这里是
String)匹配。 - 适用于所有 Iterable:任何实现了
Iterable接口的集合(包括ArrayList,LinkedList,HashSet等)都可以使用这种方式。
缺点
- 无法获取索引:如果需要在遍历过程中知道当前元素的索引,此方法无能为力。
- 无法修改集合:在遍历过程中,不能安全地使用
remove()方法删除元素(除非使用迭代器的remove()方法,见方法四)。 - 性能:在底层,它实际上是通过迭代器实现的,对于
ArrayList其性能可能略低于经典的for循环,但差距微乎其微,通常可以忽略不计。
使用 Iterator(迭代器)
Iterator 是 Java 集合框架中专门用于遍历元素的接口,它是“安全”遍历的保证,尤其是在需要修改集合时。

代码示例
List<String> fruits = new ArrayList<>();
// ... (添加元素)
System.out.println("\n--- 方法三:使用 Iterator ---");
// 获取迭代器
java.util.Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
System.out.println(fruit);
// 示例:在遍历时安全地删除元素
// if ("Orange".equals(fruit)) {
// iterator.remove(); // 使用迭代器的 remove() 方法
// }
}
优点
- 线程安全(相对):
Iterator提供了一种标准的、安全的方式来遍历集合,即使在单线程环境下,也能保证遍历过程的正确性。 - 安全的删除操作:这是它最大的优点,如果在遍历过程中需要删除元素,必须使用
iterator.remove()方法,直接在集合上调用list.remove()会抛出ConcurrentModificationException异常。 - 通用性:所有集合都支持迭代器。
缺点
- 代码稍显繁琐:相比 For-Each 循环,需要多写几行代码来获取迭代器和编写
while循环。 - 无法获取索引:同样,无法直接获取当前元素的索引。
Java 8+ Stream API
这是 Java 8 引入的函数式编程方式,功能非常强大,不仅能遍历,还能进行复杂的链式操作(如过滤、映射、聚合等)。
代码示例
List<String> fruits = new ArrayList<>();
// ... (添加元素)
System.out.println("\n--- 方法四:Java 8+ Stream API ---");
// 1. 简单遍历(消费)
fruits.stream().forEach(fruit -> System.out.println(fruit));
// 2. 更简洁的遍历(方法引用)
System.out.println("\n使用方法引用:");
fruits.stream().forEach(System.out::println);
// 3. 带条件的遍历(只打印长度大于 5 的水果)
System.out.println("\n只打印长度大于 5 的水果:");
fruits.stream()
.filter(fruit -> fruit.length() > 5)
.forEach(System.out::println);
优点
- 功能强大:不仅仅是遍历,可以无缝衔接
filter(),map(),sorted(),collect()等各种操作,实现复杂的数据处理逻辑。 - 代码简洁(函数式风格):对于复杂的操作,使用 Lambda 表达式和方法引用可以使代码非常简洁和富有表现力。
- 并行处理:只需将
stream()换成parallelStream(),即可轻松实现多线程并行处理,提高大数据量下的处理速度。
缺点
- 学习成本:对于不熟悉函数式编程的开发者来说,需要一定的学习曲线。
- 性能开销:对于简单的遍历任务,Stream API 会引入一定的额外开销(如创建流、Lambda 表达式调用等),其性能可能不如传统的 For-Each 或经典 for 循环。
- 修改集合困难:Stream API 本身设计为“管道-过滤器”模式,主要用于数据转换和消费,而不是修改源集合。
总结与最佳实践
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 经典 for 循环 | 直观,支持随机访问 | 代码冗长,不适用于 LinkedList |
需要索引或随机访问元素的简单遍历。 |
| 增强 for 循环 | 代码简洁,可读性高,通用性强 | 无法获取索引,不能安全删除 | 绝大多数情况下的首选,简单、干净、高效。 |
| Iterator | 可以安全删除元素,通用性强 | 代码稍显繁琐,无法获取索引 | 需要在遍历过程中删除元素时,必须使用。 |
| Stream API | 功能极其强大,支持并行和函数式操作 | 学习成本高,简单遍历有性能开销 | 复杂的数据处理、转换、聚合操作;需要并行计算时。 |
如何选择?
-
日常遍历,只读或修改元素:直接使用 增强 for 循环 (For-Each),这是最常用、最推荐的写法。
for (String fruit : fruits) { // do something } -
需要在遍历时删除元素:必须使用 Iterator。
Iterator<String> it = fruits.iterator(); while (it.hasNext()) { String fruit = it.next(); if (shouldRemove(fruit)) { it.remove(); // 安全删除 } } -
需要知道元素的索引:使用 经典 for 循环。
(图片来源网络,侵删)for (int i = 0; i < fruits.size(); i++) { System.out.println("Index " + i + ": " + fruits.get(i)); } -
需要对集合进行复杂的操作(过滤、转换、统计等):使用 Stream API。
List<String> longFruits = fruits.stream() .filter(f -> f.length() > 5) .collect(Collectors.toList());
