Skip to content

Lambda 遍历:Stream 与方法引用

Lambda + 集合遍历

JDK 8 引入的 Lambda 表达式让集合遍历变得更简洁。Lambda 本质上是一个「匿名函数」,可以赋值给变量、作为参数传递。

java
// JDK 7 之前:匿名内部类
list.forEach(new Consumer<String>() {
    @Override
    public void accept(String s) {
        System.out.println(s);
    }
});

// JDK 8+:Lambda 表达式
list.forEach(s -> System.out.println(s));

Lambda 的几种形式

java
List<String> list = Arrays.asList("apple", "banana", "cherry");

// 形式 1:完整 lambda
Consumer<String> c1 = (String s) -> { System.out.println(s); };

// 形式 2:类型推断(省略类型)
Consumer<String> c2 = (s) -> { System.out.println(s); };

// 形式 3:单参数可省略括号
Consumer<String> c3 = s -> { System.out.println(s); };

// 形式 4:单语句可省略大括号
Consumer<String> c4 = s -> System.out.println(s);

// 形式 5:方法引用(最简洁)
Consumer<String> c5 = System.out::println;

方法引用

方法引用是 Lambda 的「快捷写法」:

java
// 静态方法引用
list.forEach(Objects::isNull);

// 实例方法引用
list.forEach(System.out::println);

// 构造方法引用
List<String> upper = list.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());

// 自定义类的方法引用
class Util {
    static void print(String s) { System.out.println(s); }
}
list.forEach(Util::print);

Stream API:链式数据处理

Stream 是对集合的「视图」,不存储数据,只描述操作:

集合 ──创建──→ Stream ──中间操作──→ Stream ──终止操作──→ 结果

                    ├── filter  (过滤)
                    ├── map     (转换)
                    ├── sorted  (排序)
                    └── distinct (去重)

filter:过滤

java
// 保留偶数
List<Integer> evens = numbers.stream()
    .filter(n -> n % 2 == 0)
    .collect(Collectors.toList());

// 保留长度 > 4 的字符串
List<String> longWords = words.stream()
    .filter(s -> s.length() > 4)
    .collect(Collectors.toList());

map:转换

java
// 字符串列表 → 长度列表
List<Integer> lengths = words.stream()
    .map(String::length)
    .collect(Collectors.toList());

// 用户列表 → 姓名列表
List<String> names = users.stream()
    .map(User::getName)
    .collect(Collectors.toList());

链式组合

java
// 过滤 + 转换 + 收集
List<String> result = words.stream()
    .filter(s -> s.length() > 3)      // 过滤长度 > 3
    .map(String::toUpperCase)         // 转大写
    .sorted()                         // 排序
    .distinct()                       // 去重
    .collect(Collectors.toList());

Lambda 的常见错误

1. 在 Lambda 中修改外部变量

java
int sum = 0;
// ❌ JDK 8 中 lambda 不能修改外部变量
// list.forEach(x -> sum += x); // 编译错误

// ✅ JDK 16+ 可以(但仍建议避免)
// list.forEach(x -> { sum += x; });

// ✅ 推荐:reduce 或 sum()
int total = list.stream().mapToInt(Integer::intValue).sum();

2. Stream 被意外消费

java
Stream<String> stream = list.stream()
    .filter(s -> s.length() > 3);

// ✅ 正确:只消费一次
stream.forEach(System.out::println);

// ❌ 错误:Stream 不能重复消费
stream.collect(Collectors.toList()); // UnsupportedOperationException

3. 并行 Stream 的顺序问题

java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// ❌ parallelStream 不保证顺序
numbers.parallelStream()
    .forEach(System.out::print); // 输出顺序不确定

// ✅ 按顺序并行处理
numbers.parallelStream()
    .forEachOrdered(System.out::print); // 输出:12345

性能注意事项

小数据量:差异可忽略

java
// for-each、forEach、Stream.forEach 在小数据量下性能几乎相同
list.forEach(System.out::println);
list.stream().forEach(System.out::println);

大数据量:parallelStream 可加速

java
// 串行处理 1000 万元素
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);

// 并行处理
start = System.nanoTime();
long sum2 = list.parallelStream().mapToLong(Integer::intValue).sum();
System.out.printf("并行: %.0f ms%n", (System.nanoTime() - start) / 1_000_000.0);

// 并行通常快 2-4 倍(取决于 CPU 核心数)

总结

要点说明
Lambda 形式s -> expr / s -> { stmt; } / Type::method
方法引用最简洁,Class::methodobj::method
Stream 中间操作filter、map、sorted、distinct(惰性执行)
Stream 终止操作forEach、collect、reduce(触发执行)
并行parallelStream() 适合大数据量
局限性不支持 break/continue,不能修改外部变量

一句话:Lambda + Stream 是遍历的「高级写法」——让数据处理变成流水线式的链式操作。


相关链接

基于 VitePress 构建