Skip to content

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

还有 BiFunctionBinaryOperatorToIntFunction 等扩展。

二、Lambda 表达式

1. 语法

java
(参数列表) -> { 方法体 }
  • 参数类型可省(推断)。
  • 单参数可省括号。
  • 单语句可省 {} return
java
Runnable r = () -> System.out.println("run");
Comparator<Integer> c = (a, b) -> a - b;

2. Lambda 与匿名内部类区别

  • Lambda 只能用于函数式接口;匿名内部类可用于类/接口(多个抽象方法)。
  • Lambda 没有自己的 thisthis 指向外层类。
  • 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. 三步走

  1. 获取流list.stream() / Arrays.stream(arr) / Stream.of(...)
  2. 中间操作:返回 Stream,可链式
  3. 终结操作:产生结果,流被消耗
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));

练习建议

  1. 用 Lambda 替换所有匿名内部类写法。
  2. 给定 List<User>,用 Stream:
    • 找出年龄大于 18 的所有人名。
    • 按部门分组。
    • 求平均薪资、最高薪资。
    • 收集为 Map<id, User>
  3. reduceList<Integer> 的乘积。
  4. Optional 重写获取嵌套属性的代码。