Skip to content

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 是遍历的「速记法」——简单场景下最方便,但不适合需要控制流程的情况。


相关链接

基于 VitePress 构建