06-Lambda 表达式与 Stream 流
对应原始资料:
JavaSE进阶_day08
一、函数式接口
有且只有一个抽象方法的接口(可以有默认/静态方法)。用 @FunctionalInterface 校验。
java
@FunctionalInterface
interface Calculator { int calc(int a, int b); }JDK 内置四大函数式接口(java.util.function):
| 接口 | 方法 | 含义 |
|---|---|---|
Supplier<T> | T get() | 生产者,无参有返回 |
Consumer<T> | void accept(T) | 消费者,有参无返回 |
Predicate<T> | boolean test(T) | 断言,做判断 |
Function<T,R> | R apply(T) | 转换,T → R |
还有 BiFunction、BinaryOperator、ToIntFunction 等扩展。
二、Lambda 表达式
1. 语法
java
(参数列表) -> { 方法体 }- 参数类型可省(推断)。
- 单参数可省括号。
- 单语句可省
{} return。
java
Runnable r = () -> System.out.println("run");
Comparator<Integer> c = (a, b) -> a - b;2. Lambda 与匿名内部类区别
- Lambda 只能用于函数式接口;匿名内部类可用于类/接口(多个抽象方法)。
- Lambda 没有自己的
this,this指向外层类。 - Lambda 效率略高(不生成单独 class)。
3. 方法引用(JDK 8)
当 Lambda 体只是调用某个方法时,可简化为方法引用:
java
list.forEach(s -> System.out.println(s));
list.forEach(System.out::println); // 实例方法引用
list.stream().map(s -> s.length());
list.stream().map(String::length); // 类名实例方法
Stream.generate(() -> new Student());
Stream.generate(Student::new); // 构造方法引用
Integer::sum; // 静态方法引用三、Stream 流(重点)
1. 为什么用 Stream
对集合/数组进行声明式的批量操作(过滤、映射、统计),代码简洁、可并行。
2. 三步走
- 获取流:
list.stream()/Arrays.stream(arr)/Stream.of(...) - 中间操作:返回 Stream,可链式
- 终结操作:产生结果,流被消耗
java
list.stream()
.filter(s -> s.startsWith("张"))
.map(String::length)
.forEach(System.out::println);3. 常用中间操作
| 方法 | 说明 |
|---|---|
filter(Predicate) | 过滤 |
map(Function) | 映射转换 |
flatMap | 把流扁平化(如把 List<List> 拆开) |
distinct() | 去重(依赖 hashCode/equals) |
sorted() | 排序 |
sorted(Comparator) | 自定义排序 |
limit(n) | 取前 n |
skip(n) | 跳过 n |
peek(Consumer) | 窥视(调试用) |
4. 终结操作
| 方法 | 说明 |
|---|---|
forEach(Consumer) | 遍历 |
count() | 计数 |
collect(Collector) | 收集为 List/Set/Map |
reduce(BinaryOperator) | 归约(求和、求积) |
max / min / sum / average | 统计 |
anyMatch / allMatch / noneMatch | 短路判断 |
findFirst / findAny | 查找 |
5. 经典用法
java
// 求和
int sum = list.stream().mapToInt(Integer::intValue).sum();
// 收集为 List
List<String> names = list.stream().map(User::getName).collect(Collectors.toList());
// 分组
Map<String, List<User>> byDept =
list.stream().collect(Collectors.groupingBy(User::getDept));
// 分区
Map<Boolean, List<Integer>> parts =
list.stream().collect(Collectors.partitioningBy(n -> n > 10));
// 拼接字符串
String s = list.stream().collect(Collectors.joining(", ", "[", "]"));
// 归约求和
int total = list.stream().reduce(0, Integer::sum);6. 并行流
java
list.parallelStream().forEach(...);
list.stream().parallel().forEach(...);并行流底层用 ForkJoinPool(commonPool),适合数据量大、无状态、无依赖的操作。线程安全问题需自行保证(避免修改共享集合)。
四、Optional(防空指针)
java
Optional<User> opt = Optional.ofNullable(user);
String name = opt.map(User::getName).orElse("默认名");
opt.ifPresent(u -> System.out.println(u));练习建议
- 用 Lambda 替换所有匿名内部类写法。
- 给定
List<User>,用 Stream:- 找出年龄大于 18 的所有人名。
- 按部门分组。
- 求平均薪资、最高薪资。
- 收集为
Map<id, User>。
- 用
reduce求List<Integer>的乘积。 - 用
Optional重写获取嵌套属性的代码。