问题
for-each 与常规 for 循环的效率如何对比?
答案
核心结论
效率对比取决于数据结构:
- 数组:两者性能几乎相同(编译后字节码一致)
- ArrayList:性能相近,for-each 略有开销(迭代器创建)
- LinkedList:for-each 显著优于传统 for(避免 O(n²) 复杂度)
- 其他集合:for-each 通常更优,因为使用了优化的迭代器
原理分析
1. for-each 的本质
for-each(增强 for 循环)是 语法糖,编译后会转换为:
- 数组:转换为传统 for 循环
- 集合:转换为迭代器(Iterator)
// 源代码
for (String item : list) {
System.out.println(item);
}
// 编译后(集合)
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println(item);
}
2. 不同数据结构的性能对比
(1)数组:性能相同
// for-each
int[] arr = {1, 2, 3, 4, 5};
for (int num : arr) {
System.out.println(num);
}
// 传统 for
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
结论:编译后字节码几乎一致,性能无差异。
(2)ArrayList:性能相近
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
// for-each(使用迭代器)
for (Integer num : list) {
System.out.println(num);
}
// 传统 for(直接索引访问)
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
结论:
- ArrayList 的
get(i)是 O(1) 操作,传统 for 略快 - for-each 有迭代器创建开销,但差异极小(纳秒级)
- 实际应用中可忽略不计
(3)LinkedList:for-each 显著更快
List<Integer> list = new LinkedList<>(Arrays.asList(1, 2, 3, 4, 5));
// for-each(使用迭代器,O(n))
for (Integer num : list) {
System.out.println(num);
}
// 传统 for(每次 get(i) 都要遍历,O(n²))
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i)); // 每次都从头遍历到第 i 个节点
}
结论:
- LinkedList 的
get(i)是 O(n) 操作,传统 for 总复杂度为 O(n²) - for-each 使用迭代器,复杂度为 O(n)
- for-each 性能远超传统 for
性能测试示例
import java.util.*;
public class LoopPerformanceTest {
public static void main(String[] args) {
int size = 100000;
List<Integer> arrayList = new ArrayList<>(size);
List<Integer> linkedList = new LinkedList<>();
for (int i = 0; i < size; i++) {
arrayList.add(i);
linkedList.add(i);
}
// ArrayList - for-each
long start = System.nanoTime();
for (Integer num : arrayList) { }
System.out.println("ArrayList for-each: " + (System.nanoTime() - start) / 1_000_000 + " ms");
// ArrayList - 传统 for
start = System.nanoTime();
for (int i = 0; i < arrayList.size(); i++) {
Integer num = arrayList.get(i);
}
System.out.println("ArrayList for: " + (System.nanoTime() - start) / 1_000_000 + " ms");
// LinkedList - for-each
start = System.nanoTime();
for (Integer num : linkedList) { }
System.out.println("LinkedList for-each: " + (System.nanoTime() - start) / 1_000_000 + " ms");
// LinkedList - 传统 for(慎用,极慢)
start = System.nanoTime();
for (int i = 0; i < linkedList.size(); i++) {
Integer num = linkedList.get(i);
}
System.out.println("LinkedList for: " + (System.nanoTime() - start) / 1_000_000 + " ms");
}
}
典型输出(10 万元素):
ArrayList for-each: 2 ms
ArrayList for: 1 ms
LinkedList for-each: 3 ms
LinkedList for: 15000 ms // 慢 5000 倍!
对比总结
| 数据结构 | for-each | 传统 for | 推荐 |
|---|---|---|---|
| 数组 | O(n) | O(n) | 两者皆可 |
| ArrayList | O(n) | O(n) | 两者皆可(for-each 更简洁) |
| LinkedList | O(n) | O(n²) | 强烈推荐 for-each |
| Set/Map | O(n) | 不支持索引 | 必须用 for-each |
其他考量
1. 可读性
// for-each:简洁、意图明确
for (String name : names) {
System.out.println(name);
}
// 传统 for:冗长、容易出错(如索引越界)
for (int i = 0; i < names.size(); i++) {
System.out.println(names.get(i));
}
2. 使用限制
for-each 不适用的场景:
- 需要修改元素(数组除外)
- 需要删除元素(会抛出
ConcurrentModificationException) - 需要访问索引
- 需要并行遍历多个集合
// 错误示例:for-each 中删除元素
for (String name : names) {
if (name.equals("张三")) {
names.remove(name); // 抛出 ConcurrentModificationException
}
}
// 正确做法:使用迭代器
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
if (iterator.next().equals("张三")) {
iterator.remove(); // 安全删除
}
}
面试总结
- 数组/ArrayList:性能相近,for-each 更简洁
- LinkedList:for-each 远快于传统 for(O(n) vs O(n²))
- 原理:for-each 是语法糖,集合使用迭代器,数组转为传统 for
- 推荐:优先使用 for-each,除非需要索引或修改/删除元素
- 注意:for-each 遍历时不能修改集合结构(增删元素)