16 Lambda 与 Stream¶
本章目标¶
- 理解函数式接口
- 掌握 Lambda 表达式写法
- 掌握方法引用
- 掌握 Stream API 的常用操作
16.1 函数式接口¶
只有一个抽象方法的接口,叫函数式接口(可以用 @FunctionalInterface 标记)。它的实现可以用 Lambda 简洁书写。
@FunctionalInterface
public interface Calculator {
int calc(int a, int b); // 只有一个抽象方法
}
16.2 Lambda 表达式¶
Lambda 就是"匿名函数"的简洁写法:(参数) -> 表达式/代码块
// 传统匿名类
Calculator add = new Calculator() {
@Override
public int calc(int a, int b) {
return a + b;
}
};
// Lambda
Calculator add = (a, b) -> a + b;
// 使用
System.out.println(add.calc(3, 4)); // 7
Lambda 各种写法¶
// 一个参数:可以省略括号
x -> x * 2
// 多个参数
(a, b) -> a + b
// 多条语句:用 {} 包起来
(a, b) -> {
int sum = a + b;
System.out.println("sum = " + sum);
return sum;
}
// 无参数
() -> System.out.println("hello")
Java 内置函数式接口(java.util.function)¶
| 接口 | 抽象方法 | 用途 |
|---|---|---|
Function<T,R> |
R apply(T) |
转换:输入 T 输出 R |
Consumer<T> |
void accept(T) |
消费:处理一个值无返回 |
Supplier<T> |
T get() |
供应:无参返回一个值 |
Predicate<T> |
boolean test(T) |
判断:返回布尔 |
Runnable |
void run() |
无参无返回 |
Function<String, Integer> toLen = s -> s.length();
toLen.apply("hello"); // 5
Consumer<String> printer = s -> System.out.println(s);
printer.accept("你好");
Supplier<Double> random = () -> Math.random();
random.get();
Predicate<Integer> isEven = n -> n % 2 == 0;
isEven.test(4); // true
16.3 排序的 Lambda 应用(模组常用)¶
List<Player> players = new ArrayList<>();
// 按等级降序
players.sort((p1, p2) -> p2.getLevel() - p1.getLevel());
// 按名字排序
players.sort((p1, p2) -> p1.getName().compareTo(p2.getName()));
// 按等级升序
players.sort(Comparator.comparingInt(Player::getLevel));
16.4 方法引用¶
方法已经存在时,直接用 类名::方法名 引用:
List<String> names = List.of("小明", "小红");
// Lambda
names.forEach(s -> System.out.println(s));
// 方法引用(等价)
names.forEach(System.out::println);
// 静态方法引用
Function<String, Integer> f = Integer::parseInt; // 等价 s -> Integer.parseInt(s)
// 实例方法引用
names.sort(String::compareTo);
16.5 Stream API¶
Stream 是对集合的流水线式处理:创建流 → 中间操作(可多个)→ 终止操作。
List<Integer> numbers = List.of(5, 3, 8, 1, 9, 3, 2);
// 过滤偶数 -> 排序 -> 去重 -> 收集成列表
List<Integer> result = numbers.stream()
.filter(n -> n % 2 == 0) // 中间:过滤
.sorted() // 中间:排序
.distinct() // 中间:去重
.toList(); // 终止:收集
// [2, 8]
常用操作¶
// filter:过滤
players.stream().filter(p -> p.getLevel() >= 30)
// map:转换(提取字段)
List<String> names = players.stream()
.map(Player::getName)
.toList();
// forEach:遍历
players.stream().forEach(p -> System.out.println(p.getName()));
// count:计数
long count = players.stream().filter(p -> p.getHp() <= 0).count();
// anyMatch / allMatch / noneMatch:判断
boolean anyOp = players.stream().anyMatch(Player::isOp);
// findFirst:取第一个
Player first = players.stream().findFirst().orElse(null);
// reduce:聚合(求和)
int total = numbers.stream().reduce(0, Integer::sum);
// max / min
Optional<Integer> max = numbers.stream().max(Integer::compareTo);
// collect 到各种容器
List<Player> list = players.stream().filter(...).toList();
Set<String> set = players.stream().map(Player::getName).collect(Collectors.toSet());
Map<String, Integer> map = players.stream()
.collect(Collectors.toMap(Player::getName, Player::getLevel));
Stream 不修改原集合
stream() 的操作产生新结果,原集合不变(除非你用 forEach 改对象内部)。
并行流
list.parallelStream() 多线程处理,数据量大时可能更快;但要注意线程安全,新手慎用。
16.6 实战:模组场景¶
// 从在线玩家中筛选出等级最高的 5 个管理员
List<Player> topOps = onlinePlayers.stream()
.filter(Player::isOp)
.sorted((p1, p2) -> p2.getLevel() - p1.getLevel())
.limit(5)
.toList();
// 统计掉落表总权重
double totalWeight = dropTable.stream()
.mapToDouble(DropEntry::getWeight)
.sum();
// 给所有怪物施加 3 秒缓慢效果(用 forEach)
mobs.stream().forEach(m -> m.addEffect(new MobEffectInstance(Effects.MOVEMENT_SLOWDOWN, 60, 1)));
16.7 小练习¶
- 用 Lambda 给玩家列表按血量升序排序
- 用 Stream:从 1~100 的 List 里筛出能被 3 整除的,求它们的平方和
- 用 map 把字符串列表转成大写列表
- 把玩家列表按等级分组(Collectors.groupingBy)
本章小结¶
- 函数式接口 = 一个抽象方法的接口,可用 Lambda
- Lambda 语法:
(参数) -> 表达式 - 方法引用
类::方法更简洁 - Stream 流水线:filter/map/sorted 中间操作 + toList/collect 终止操作
- Stream 不修改原集合
下一章:17 多线程基础