Lambda 与 Stream API
lambda、方法引用、Stream 链式、Collectors、并行流
为什么用 Lambda 与 Stream
应用场景:处理集合 / 流式数据时,传统 for 循环 + 临时变量的写法又长又容易出错。Lambda + Stream 让你用"说明意图"的方式写:过滤、映射、分组、聚合都是一行操作,更接近数学公式而不是步骤。
// WhyStream.java
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class WhyStream {
record User(String name, int age, String dept) {}
public static void main(String[] args) {
List<User> users = List.of(
new User("Alice", 30, "Eng"),
new User("Bob", 25, "Sales"),
new User("Carol", 35, "Eng"),
new User("Dave", 40, "Sales")
);
// 命令式:找 Eng 部门、年龄 >= 30 的名字
List<String> imperative = new ArrayList<>();
for (User u : users) {
if (u.dept().equals("Eng") && u.age() >= 30) {
imperative.add(u.name());
}
}
System.out.println(imperative);
// 声明式:用 Stream,一目了然
List<String> declarative = users.stream()
.filter(u -> u.dept().equals("Eng"))
.filter(u -> u.age() >= 30)
.map(User::name)
.collect(Collectors.toList());
System.out.println(declarative);
}
}Lambda 表达式语法
lambda 是函数式接口(单抽象方法接口)的简写。形式:(参数列表) -> 表达式 或 (参数列表) -> { 语句块; return 值; }。参数类型一般可省,编译器从上下文推断。
// LambdaSyntax.java
import java.util.function.*;
public class LambdaSyntax {
public static void main(String[] args) {
// 无参
Supplier<String> greet = () -> "hello";
System.out.println(greet.get());
// 单参(可省括号)
Function<Integer, Integer> square = x -> x * x;
System.out.println(square.apply(5));
// 多参
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
System.out.println(add.apply(2, 3));
// 语句块
Predicate<String> validEmail = s -> {
if (s == null) return false;
int at = s.indexOf('@');
return at > 0 && at < s.length() - 1;
};
System.out.println(validEmail.test("a@b.c"));
}
}方法引用四种形态
当 lambda 体只是"直接调用一个已有方法"时,可以写成方法引用,更简洁。
// MethodRef.java
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
public class MethodRef {
public static void main(String[] args) {
// 1) 类的静态方法:Integer::parseInt 等价于 s -> Integer.parseInt(s)
Function<String, Integer> parse = Integer::parseInt;
System.out.println(parse.apply("42"));
// 2) 实例的实例方法:System.out::println
List<String> names = List.of("a", "b", "c");
names.forEach(System.out::println);
// 3) 类的实例方法:String::toUpperCase 等价于 s -> s.toUpperCase()
Function<String, String> upper = String::toUpperCase;
System.out.println(upper.apply("hi"));
// 4) 构造方法引用:ArrayList::new
Supplier<java.util.ArrayList<String>> factory = java.util.ArrayList::new;
System.out.println(factory.get());
}
}Stream 创建
Stream 是"一次性"的:终止操作后就不能再用。常见创建方式:从集合 stream() / Arrays.stream / Stream.of / Stream.generate(无限流,需 limit)/ IntStream.range(整数区间)。
// StreamCreate.java
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamCreate {
public static void main(String[] args) {
// 1) 从集合
List<Integer> list = List.of(1, 2, 3);
list.stream().forEach(System.out::println);
// 2) 从数组
int[] arr = {1, 2, 3};
Arrays.stream(arr).forEach(System.out::println);
// 3) Stream.of
Stream.of("a", "b", "c").forEach(System.out::println);
// 4) 数字区间(左闭右开)
IntStream.range(0, 5).forEach(System.out::println);
// 5) 无限流 + limit
Stream.iterate(1, n -> n * 2).limit(5).forEach(System.out::println);
// 1 2 4 8 16
}
}中间操作:filter / map / flatMap / sorted / distinct / limit
中间操作返回新的 Stream,可链式调用。它们是"懒"的——只有遇到终止操作才会真正执行。
// StreamIntermediate.java
import java.util.List;
import java.util.stream.Collectors;
public class StreamIntermediate {
public static void main(String[] args) {
List<String> words = List.of("apple", "banana", "cherry", "avocado", "berry");
// filter:保留满足条件的
List<String> withA = words.stream()
.filter(w -> w.startsWith("a"))
.collect(Collectors.toList());
System.out.println(withA);
// map:每个元素做转换
List<Integer> lengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println(lengths);
// flatMap:每个元素变成一个 Stream,再压平
List<List<Integer>> nested = List.of(List.of(1, 2), List.of(3, 4));
List<Integer> flat = nested.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(flat); // [1, 2, 3, 4]
// sorted + distinct + limit 组合
List<Integer> top3 = List.of(3, 1, 4, 1, 5, 9, 2, 6, 5).stream()
.distinct()
.sorted(java.util.Comparator.reverseOrder())
.limit(3)
.collect(Collectors.toList());
System.out.println(top3); // [9, 6, 5]
}
}终止操作:forEach / collect / reduce / count / anyMatch / findFirst
终止操作触发计算,并消耗这个 Stream(之后不能再用)。返回的不再是 Stream,而是具体的值/集合/Optional。
// StreamTerminal.java
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class StreamTerminal {
public static void main(String[] args) {
List<Integer> nums = List.of(1, 2, 3, 4, 5);
// count
long even = nums.stream().filter(n -> n % 2 == 0).count();
System.out.println("even count = " + even);
// anyMatch / allMatch / noneMatch
System.out.println(nums.stream().anyMatch(n -> n > 3)); // true
System.out.println(nums.stream().allMatch(n -> n > 0)); // true
System.out.println(nums.stream().noneMatch(n -> n < 0)); // true
// findFirst:返回 Optional
Optional<Integer> first = nums.stream().filter(n -> n > 3).findFirst();
System.out.println(first.orElse(-1));
// reduce:归约
int sum = nums.stream().reduce(0, Integer::sum);
System.out.println("sum = " + sum);
// collect 到 List
List<Integer> doubled = nums.stream()
.map(n -> n * 2)
.collect(Collectors.toList());
System.out.println(doubled);
}
}Collectors:分组、统计、拼接
Collectors 是 Stream.collect 的"插件",提供分组(groupingBy)、按 key 收成 Map(toMap)、字符串拼接(joining)、统计(counting / summingInt / averagingDouble)等终止操作。**实际开发用 Stream 最多的就是这块。**
// CollectorsDemo.java
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class CollectorsDemo {
record Order(String user, String product, int amount) {}
public static void main(String[] args) {
List<Order> orders = List.of(
new Order("Alice", "book", 30),
new Order("Bob", "book", 25),
new Order("Alice", "pen", 10),
new Order("Carol", "book", 60),
new Order("Bob", "pen", 5)
);
// 1) 拼接:所有用户名(去重 + 逗号)
String names = orders.stream()
.map(Order::user)
.distinct()
.collect(Collectors.joining(", "));
System.out.println(names); // Alice, Bob, Carol
// 2) 按用户分组
Map<String, List<Order>> byUser = orders.stream()
.collect(Collectors.groupingBy(Order::user));
System.out.println(byUser);
// 3) 按用户聚合总金额
Map<String, Integer> totalByUser = orders.stream()
.collect(Collectors.groupingBy(
Order::user,
Collectors.summingInt(Order::amount)
));
System.out.println(totalByUser); // {Alice=40, Bob=30, Carol=60}
// 4) 按产品计数
Map<String, Long> countByProduct = orders.stream()
.collect(Collectors.groupingBy(
Order::product,
Collectors.counting()
));
System.out.println(countByProduct);
// 5) 用户名 -> 总金额(toMap)
Map<String, Integer> totalMap = orders.stream()
.collect(Collectors.toMap(
Order::user,
Order::amount,
Integer::sum // 同一个 key 多次出现时合并函数
));
System.out.println(totalMap);
}
}IntStream / LongStream / DoubleStream
数字专用流,避免装箱拆箱开销。提供 sum / average / max / min / summaryStatistics 这些数值聚合方法。
// NumericStream.java
import java.util.IntSummaryStatistics;
import java.util.stream.IntStream;
public class NumericStream {
public static void main(String[] args) {
int sum = IntStream.rangeClosed(1, 100).sum();
System.out.println("1..100 sum = " + sum); // 5050
IntSummaryStatistics stats = IntStream.of(3, 1, 4, 1, 5, 9, 2, 6).summaryStatistics();
System.out.println(stats);
// IntSummaryStatistics{count=8, sum=31, min=1, average=3.875000, max=9}
// 普通 Stream<Integer> 转 IntStream
int total = java.util.List.of(1, 2, 3).stream().mapToInt(Integer::intValue).sum();
System.out.println(total);
}
}并行流:parallelStream() 何时用
调 .parallelStream() 或 .parallel() 让数据被多核同时处理。**注意:不是越快越好。**只在数据量大(万级以上)、操作 CPU 密集、操作无状态时用;涉及 IO / 顺序敏感 / 共享可变状态时反而更慢更危险。
// Parallel.java
import java.util.stream.IntStream;
public class Parallel {
public static void main(String[] args) {
// 计算 1..1_000_000 平方和
long sum = IntStream.rangeClosed(1, 1_000_000)
.parallel()
.mapToLong(n -> (long) n * n)
.sum();
System.out.println(sum);
}
}实战:从订单聚合 Top-3 用户
组合 filter + groupingBy + summingInt + sorted + limit 的常见业务场景:从订单数据找消费 Top-3 用户。
// TopUsers.java
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class TopUsers {
record Order(String user, String product, int amount, String status) {}
public static void main(String[] args) {
List<Order> orders = List.of(
new Order("Alice", "book", 30, "paid"),
new Order("Bob", "book", 25, "paid"),
new Order("Alice", "pen", 10, "refund"),
new Order("Carol", "book", 60, "paid"),
new Order("Dave", "pen", 90, "paid"),
new Order("Bob", "pen", 5, "paid")
);
// Top-3 已支付用户消费额
List<Map.Entry<String, Integer>> top3 = orders.stream()
.filter(o -> o.status().equals("paid"))
.collect(Collectors.groupingBy(Order::user, Collectors.summingInt(Order::amount)))
.entrySet().stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.limit(3)
.collect(Collectors.toList());
top3.forEach(e -> System.out.println(e.getKey() + " -> " + e.getValue()));
// Dave -> 90, Carol -> 60, Bob -> 30
}
}