两种循环的基本语法
传统 for 循环
这是最经典的 for 循环,也被称为 C-style for loop(C 风格 for 循环)。

语法结构:
for (初始化语句; 循环条件; 更新语句) {
// 循环体
}
执行流程:
- 初始化:首先执行
初始化语句,通常用于声明一个循环计数器(如int i = 0),这部分只会在循环开始时执行一次。 - 条件判断:接着执行
循环条件,如果条件为true,则进入循环体,执行其中的代码,如果条件为false,则循环终止。 - 循环体:执行
for循环大括号 内的代码。 - 更新:执行完循环体后,执行
更新语句(如i++),然后返回到第 2 步,再次进行条件判断。
示例:
int[] numbers = {1, 2, 3, 4, 5};
System.out.println("使用传统 for 循环打印数组元素及其索引:");
for (int i = 0; i < numbers.length; i++) {
System.out.println("索引: " + i + ", 值: " + numbers[i]);
}
增强型 for 循环 (for-each)
Java 5 引入了增强型 for 循环,旨在简化遍历数组或集合的操作。

语法结构:
for (元素类型 元素变量 : 遍历对象) {
// 循环体
}
执行流程:
它内部会自动处理迭代过程,你无需关心索引的初始化、边界判断和更新,它会依次从 遍历对象(数组或集合)中取出每一个元素,赋值给 元素变量,然后执行一次循环体,直到所有元素都被遍历完毕。
示例:
String[] fruits = {"Apple", "Banana", "Orange"};
System.out.println("使用 for-each 循环打印水果列表:");
for (String fruit : fruits) {
System.out.println(fruit);
}
核心区别与对比
为了更清晰地展示差异,我们可以从以下几个维度进行对比:

| 特性 | 传统 for 循环 |
增强型 for 循环 (for-each) |
|---|---|---|
| 语法 | for (int i=0; i<arr.length; i++) |
for (int item : arr) |
| 用途 | 通用循环,可用于任何需要控制循环次数的场景 | 专门用于遍历(数组、集合等) |
| 索引访问 | 可以,通过循环变量 i 可以轻松获取当前元素的索引 |
不可以,无法直接获取当前元素的索引或位置 |
| 修改集合 | 可以,可以通过索引 arr[i] = newValue 修改数组/集合中的元素 |
不可以。item 是一个临时变量,修改它不会影响原始集合中的元素,对于 List 等集合,使用 remove() 方法会抛出 ConcurrentModificationException。 |
| 灵活性 | 高,可以自由控制循环方向(i--)、步长(i += 2),甚至可以嵌套复杂的逻辑 |
低,只能从头到尾按顺序遍历。 |
| 底层实现 | 基于计数器和条件判断 | 基于 Iterable 接口(集合)或数组(对于数组,编译器会将其转换为传统的 for 循环,使用 java.util.Iterator) |
| 适用数据类型 | 任何可迭代的对象,以及非迭代场景(如固定次数循环) | 仅限数组或实现了 Iterable 接口的集合 |
详细讲解关键区别
索引访问
这是两者最显著的区别之一。
-
传统
for循环:天生支持索引。List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); for (int i = 0; i < names.size(); i++) { // 可以根据索引做很多事情,比如获取前一个元素 if (i > 0) { System.out.println(names.get(i) + " 的前一个名字是 " + names.get(i - 1)); } } -
for-each循环:不提供索引,如果你需要索引,通常需要借助一个外部变量来模拟,但这会破坏for-each的简洁性。List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); int index = 0; for (String name : names) { System.out.println(name + " 的索引是 " + index); index++; }
修改集合
这是一个非常重要的坑点。
-
修改数组元素:两者都可以通过索引修改数组元素。
int[] numbers = {1, 2, 3}; // 传统 for for (int i = 0; i < numbers.length; i++) { numbers[i] *= 2; // 修改成功 } // for-each (通过外部索引) int j = 0; for (int num : numbers) { numbers[j] = num * 2; // 修改成功 j++; }注意:直接在
for-each循环中修改num是无效的,num是一个副本。 -
修改集合(
List,Set等):-
传统
for循环:可以通过list.set(i, newValue)修改集合中的元素。 -
for-each循环:绝对不要在for-each循环中使用list.remove()或collection.add()等方法来修改集合本身,这会抛出java.util.ConcurrentModificationException异常。List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)); // 错误示范!会抛出 ConcurrentModificationException for (Integer i : list) { if (i % 2 == 0) { list.remove(i); // 报错! } }为什么?
for-each循环内部使用了一个迭代器(Iterator),当你调用list.remove()时,你直接修改了底层数据结构,而迭代器并不知道这个变化,它认为自己还在一个“安全”的、未被修改的集合上遍历,因此它会抛出异常来告诉你集合被并发修改了。 -
如何安全地在遍历中删除元素? 应该使用
Iterator的remove()方法。List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)); Iterator<Integer> iterator = list.iterator(); while (iterator.hasNext()) { Integer i = iterator.next(); if (i % 2 == 0) { iterator.remove(); // 安全地删除当前元素 } } System.out.println(list); // 输出: [1, 3, 5]从 Java 8 开始,你还可以使用
removeIf方法,这是最简洁、最安全的方式:list.removeIf(i -> i % 2 == 0);
-
如何选择?(最佳实践)
-
优先使用
for-each循环- 当你只需要遍历一个数组或集合,并且不需要索引、不需要修改集合、不需要反向遍历时,
for-each是你的不二之选。 - 它代码更简洁、可读性更高,并且能有效避免
ArrayIndexOutOfBoundsException等边界错误。
- 当你只需要遍历一个数组或集合,并且不需要索引、不需要修改集合、不需要反向遍历时,
-
在以下场景使用传统
for循环- 需要索引时:这是最常见的原因,你需要打印元素的索引,或者需要根据索引访问前一个/后一个元素。
- 需要修改集合时:虽然可以通过外部索引实现,但如果逻辑复杂,直接使用传统
for循环或Iterator更清晰。 - 需要非顺序遍历时:你需要从后往前遍历 (
for (int i = arr.length - 1; i >= 0; i--)),或者按步长遍历 (for (int i = 0; i < arr.length; i += 2))。 - 循环体不依赖于遍历对象时:一个简单的固定次数循环。
// 传统 for 循环非常适合这种场景 for (int i = 0; i < 100; i++) { // 执行100次 }
| 场景 | 推荐的循环类型 | 理由 |
|---|---|---|
| 单纯遍历数组/集合 | for-each |
简洁、安全、可读性高 |
| 需要元素索引 | 传统 for |
for-each 无法直接提供索引 |
| 需要修改集合内容 | 传统 for + set() 或 Iterator |
for-each 中直接修改会抛异常 |
| 需要从后往前遍历 | 传统 for |
for-each 只能顺序遍历 |
| 需要固定次数循环 | 传统 for |
for-each 不适用,因为没有“遍历对象” |
记住这个核心思想:for-each 是为了遍历而生,而 for 循环是一个更通用的控制流工具。 在代码中,选择最合适、最清晰、最安全的工具来完成你的任务。
