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()); // UnsupportedOperationException3. 并行 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::method 或 obj::method |
| Stream 中间操作 | filter、map、sorted、distinct(惰性执行) |
| Stream 终止操作 | forEach、collect、reduce(触发执行) |
| 并行 | parallelStream() 适合大数据量 |
| 局限性 | 不支持 break/continue,不能修改外部变量 |
一句话:Lambda + Stream 是遍历的「高级写法」——让数据处理变成流水线式的链式操作。
