forEach 和 Lambda 遍历:JDK 8+ 的遍历方式
四种遍历方式的演进
JDK 1.0: Enumeration (遗留接口,已废弃)
JDK 1.2: Iterator (标准接口,统一遍历)
JDK 1.5: for-each (语法糖,底层还是 Iterator)
JDK 8+: forEach + Lambda(最简洁,推荐使用)forEach:最简洁的遍历
基本用法
java
List<String> list = Arrays.asList("apple", "banana", "cherry");
// ✅ 推荐:方法引用
list.forEach(System.out::println);
// ✅ 也可以:Lambda 表达式
list.forEach(s -> System.out.println(s));Map 的 forEach
java
Map<String, Integer> map = new HashMap<>();
map.put("apple", 1);
map.put("banana", 2);
map.put("cherry", 3);
// 遍历键值对
map.forEach((k, v) -> System.out.println(k + " = " + v));
// 遍历 key
map.keySet().forEach(System.out::println);
// 遍历 value
map.values().forEach(System.out::println);Set 的 forEach
java
Set<String> set = new LinkedHashSet<>(Arrays.asList("a", "b", "c"));
set.forEach(System.out::println); // 按插入顺序输出Stream API:可链式操作
filter + forEach
java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 打印所有偶数
numbers.stream()
.filter(n -> n % 2 == 0)
.forEach(System.out::println);map + forEach
java
// 打印所有名字的大写形式
List.of("alice", "bob", "charlie").stream()
.map(String::toUpperCase)
.forEach(System.out::println);flatMap + forEach
java
// 打印所有子列表中的元素
List<List<String>> nested = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c", "d")
);
nested.stream()
.flatMap(List::stream)
.forEach(System.out::println);forEach 的局限性
不能 break / continue
java
// ❌ forEach 不支持 break
numbers.forEach(n -> {
if (n == 5) break; // 编译错误!
});
// ✅ 替代方案:用传统循环
for (int n : numbers) {
if (n == 5) break; // OK
}
// ✅ 或者:用 Stream + limit
numbers.stream()
.limit(5)
.forEach(System.out::println);不能修改外部变量(JDK 8 的 lambda 限制)
java
int sum = 0;
// ❌ JDK 8 中,lambda 不能修改外部变量
// numbers.forEach(n -> sum += n); // 编译错误
// ✅ 替代方案 1:用 AtomicInteger
AtomicInteger sum = new AtomicInteger();
numbers.forEach(sum::addAndGet);
// ✅ 替代方案 2:用 reduce
int total = numbers.stream().reduce(0, Integer::sum);
// ✅ 替代方案 3:用传统循环
int total2 = 0;
for (int n : numbers) total2 += n;不能抛出检查异常
java
// ❌ forEach 中的 lambda 不能抛出检查异常
list.forEach(file -> {
readFile(file); // 如果 readFile 抛出 IOException,这里无法处理
});
// ✅ 替代方案:用传统循环 + try-catch
for (String file : list) {
try {
readFile(file);
} catch (IOException e) {
// 处理异常
}
}性能对比
小数据量(差异不大)
java
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) list.add(i);
long start = System.nanoTime();
int sum = 0;
// for-each
for (int n : list) sum += n;
System.out.printf("for-each: %.0f ms%n", (System.nanoTime() - start) / 1_000_000.0);
// forEach + Lambda
start = System.nanoTime();
sum = 0;
list.forEach(n -> sum += n);
System.out.printf("forEach: %.0f ms%n", (System.nanoTime() - start) / 1_000_000.0);
// 典型结果:for-each 和 forEach 性能几乎相同大数据量并行(parallelStream 更快)
java
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10_000_000; i++) list.add(i);
// 串行 Stream
long start = System.nanoTime();
long sum = list.stream().mapToLong(Integer::intValue).sum();
System.out.printf("串行: %.0f ms%n", (System.nanoTime() - start) / 1_000_000.0);
// 并行 Stream
start = System.nanoTime();
sum = list.parallelStream().mapToLong(Integer::intValue).sum();
System.out.printf("并行: %.0f ms%n", (System.nanoTime() - start) / 1_000_000.0);
// 典型结果:parallelStream 比串行快 2-4 倍(取决于 CPU 核心数)选型决策
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 简单遍历 | forEach | 最简洁 |
| 需要 break/continue | 传统循环 | forEach 不支持 |
| 链式操作(过滤/映射) | Stream API | 声明式代码 |
| 大数据量并行 | parallelStream | 多核加速 |
| 并发安全遍历 | 并发集合 | 避免 ConcurrentModificationException |
| Map 遍历 | map.forEach((k, v) -> ...) | JDK 8+ 最简洁 |
总结
| 要点 | 说明 |
|---|---|
forEach | 最简洁,适合简单遍历 |
Stream + forEach | 适合链式过滤/映射 |
parallelStream | 大数据量可加速 |
| 局限性 | 不支持 break/continue,不能修改变量 |
一句话:
forEach是遍历的「速记法」——简单场景下最方便,但不适合需要控制流程的情况。
