Java Stream 流式编程总结
Java 8 Stream API 常用操作速查与实战
一、Stream 基础概念
1.1 什么是 Stream
Stream 是 Java 8 引入的函数式数据处理 API,对集合数据进行声明式的、流水线式的操作,支持链式调用和惰性求值。
// 传统方式
List<String> result = new ArrayList<>();
for (String s : list) {
if (s.length() > 3) {
result.add(s.toUpperCase());
}
}
// Stream 方式
List<String> result = list.stream()
.filter(s -> s.length() > 3)
.map(String::toUpperCase)
.collect(Collectors.toList());
1.2 Stream 三要素
数据源 ──→ 中间操作(可多个) ──→ 终端操作(仅一个)
(集合/数组) (惰性求值,返回Stream) (触发计算,返回结果)
|
类型 |
说明 |
示例 |
|---|---|---|
|
中间操作 |
返回 Stream,惰性不触发计算 |
|
|
终端操作 |
触发计算,关闭 Stream |
|
二、创建 Stream
// 1. 从集合创建
Stream<String> stream = list.stream(); // 串行流
Stream<String> parallelStream = list.parallelStream(); // 并行流
// 2. 从数组创建
IntStream intStream = Arrays.stream(new int[]{1, 2, 3});
Stream<String> strStream = Arrays.stream(new String[]{"a", "b"});
// 3. 直接创建
Stream<Integer> s = Stream.of(1, 2, 3, 4, 5);
Stream<String> empty = Stream.empty();
// 4. 生成无限流
Stream<Double> randoms = Stream.generate(Math::random);
Stream<Integer> iterate = Stream.iterate(0, n -> n + 2); // 0,2,4,6,...
// 5. 基本类型流(避免装箱)
IntStream intStream2 = IntStream.range(1, 100); // 1~99
LongStream longStream = LongStream.rangeClosed(1, 100); // 1~100(含)
DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0);
三、中间操作
3.1 filter —— 过滤
List<Integer> evens = Stream.of(1, 2, 3, 4, 5, 6)
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// [2, 4, 6]
3.2 map —— 映射/转换
// 提取 User 的 name 字段
List<String> names = users.stream()
.map(User::getName)
.collect(Collectors.toList());
// 类型转换
List<Integer> lengths = Stream.of("aa", "bbb", "cccc")
.map(String::length)
.collect(Collectors.toList());
// [2, 3, 4]
3.3 flatMap —— 扁平化
// 将多个 list 打平成一个
List<List<Integer>> nested = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4, 5)
);
List<Integer> flat = nested.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// [1, 2, 3, 4, 5]
// 拆分字符串
List<String> words = Stream.of("hello world", "java stream")
.flatMap(s -> Arrays.stream(s.split(" ")))
.collect(Collectors.toList());
// [hello, world, java, stream]
3.4 distinct —— 去重
List<Integer> distinct = Stream.of(1, 2, 2, 3, 3, 3)
.distinct()
.collect(Collectors.toList());
// [1, 2, 3]
3.5 sorted —— 排序
// 自然排序
List<Integer> sorted = Stream.of(3, 1, 4, 1, 5)
.sorted()
.collect(Collectors.toList());
// 自定义排序
users.stream()
.sorted(Comparator.comparing(User::getAge).reversed())
.collect(Collectors.toList());
// 多条件排序
users.stream()
.sorted(Comparator.comparing(User::getAge)
.thenComparing(User::getName))
.collect(Collectors.toList());
3.6 limit / skip —— 截取
List<Integer> top3 = Stream.of(1, 2, 3, 4, 5)
.limit(3)
.collect(Collectors.toList());
// [1, 2, 3]
List<Integer> skip2 = Stream.of(1, 2, 3, 4, 5)
.skip(2)
.collect(Collectors.toList());
// [3, 4, 5]
// 分页:第2页,每页3条
List<Integer> page2 = list.stream()
.skip((2 - 1) * 3)
.limit(3)
.collect(Collectors.toList());
3.7 peek —— 调试窥探
List<String> result = Stream.of("a", "b", "c")
.peek(s -> System.out.println("处理: " + s))
.map(String::toUpperCase)
.peek(s -> System.out.println("映射后: " + s))
.collect(Collectors.toList());
四、终端操作
4.1 collect —— 收集(最常用)
// 收集为 List
List<String> list = stream.collect(Collectors.toList());
// 收集为 Set
Set<String> set = stream.collect(Collectors.toSet());
// 收集为 Map
Map<Integer, User> userMap = users.stream()
.collect(Collectors.toMap(User::getId, Function.identity()));
// 收集为 Map(处理重复 key)
Map<String, User> map2 = users.stream()
.collect(Collectors.toMap(
User::getName,
Function.identity(),
(existing, replacement) -> replacement // 保留后者
));
// 分组
Map<String, List<User>> groupByDept = users.stream()
.collect(Collectors.groupingBy(User::getDept));
// 多级分组
Map<String, Map<String, List<User>>> twoLevel = users.stream()
.collect(Collectors.groupingBy(
User::getDept,
Collectors.groupingBy(User::getGender)
));
// 分组计数
Map<String, Long> countByDept = users.stream()
.collect(Collectors.groupingBy(User::getDept, Collectors.counting()));
// 分区(true/false 两组)
Map<Boolean, List<User>> adultPartition = users.stream()
.collect(Collectors.partitioningBy(u -> u.getAge() >= 18));
// 拼接字符串
String joined = users.stream()
.map(User::getName)
.collect(Collectors.joining(", ", "[", "]"));
// [张三, 李四, 王五]
4.2 forEach —— 遍历
users.stream().forEach(System.out::println);
4.3 reduce —— 归约
// 求和
int sum = Stream.of(1, 2, 3, 4, 5)
.reduce(0, Integer::sum);
// 求最大值
Integer max = Stream.of(1, 5, 3, 9, 2)
.reduce(Integer.MIN_VALUE, Integer::max);
4.4 count —— 计数
long count = users.stream()
.filter(u -> u.getAge() > 18)
.count();
4.5 匹配(短路操作)
boolean allAdult = users.stream().allMatch(u -> u.getAge() >= 18); // 全匹配
boolean anyMale = users.stream().anyMatch(u -> "男".equals(u.getGender())); // 任一匹配
boolean noneNull = users.stream().noneMatch(u -> u.getName() == null); // 全不匹配
4.6 findFirst / findAny
// 取第一个元素
Optional<User> first = users.stream()
.filter(u -> u.getAge() > 30)
.findFirst();
// 取任意元素(并行流中更高效)
Optional<User> any = users.parallelStream()
.filter(u -> u.getAge() > 30)
.findAny();
五、数值流(减少装箱开销)
// 基本类型流
IntStream intStream = IntStream.range(1, 100);
LongStream longStream = LongStream.of(1L, 2L, 3L);
DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0);
// 常用方法
int sum = IntStream.rangeClosed(1, 100).sum(); // 求和
OptionalDouble avg = IntStream.range(1, 100).average(); // 平均值
OptionalInt max = IntStream.range(1, 100).max(); // 最大值
// 装箱:基本类型流 → 对象流
Stream<Integer> boxed = IntStream.range(1, 10).boxed();
// 拆箱:对象流 → 基本类型流
IntStream ages = users.stream().mapToInt(User::getAge);
六、Collectors 深入
// 聚合统计
IntSummaryStatistics stats = users.stream()
.collect(Collectors.summarizingInt(User::getAge));
stats.getMax(); stats.getMin(); stats.getAverage(); stats.getSum(); stats.getCount();
// reducing 自定义归约
Integer totalAge = users.stream()
.collect(Collectors.reducing(0, User::getAge, Integer::sum));
// collectingAndThen:收集后再处理
List<User> unmodifiable = users.stream()
.collect(Collectors.collectingAndThen(
Collectors.toList(),
Collections::unmodifiableList
));
// mapping:在分组前做转换
Map<String, List<Integer>> deptAges = users.stream()
.collect(Collectors.groupingBy(
User::getDept,
Collectors.mapping(User::getAge, Collectors.toList())
));
七、实战场景
7.1 List → Map
// id → User
Map<Integer, User> map = users.stream()
.collect(Collectors.toMap(User::getId, u -> u));
// name → age
Map<String, Integer> nameAge = users.stream()
.collect(Collectors.toMap(User::getName, User::getAge));
7.2 按条件分组
// 按年龄分组
Map<Integer, List<User>> byAge = users.stream()
.collect(Collectors.groupingBy(User::getAge));
// 按年龄段分组
Map<String, List<User>> byAgeRange = users.stream()
.collect(Collectors.groupingBy(u -> {
if (u.getAge() < 18) return "未成年";
else if (u.getAge() < 60) return "成年";
else return "老年";
}));
7.3 提取对象某字段去重
// 按 name 去重,保留第一个
List<User> distinctByName = users.stream()
.collect(Collectors.toMap(
User::getName,
Function.identity(),
(existing, replace) -> existing
))
.values().stream()
.collect(Collectors.toList());
7.4 排序取 Top N
List<User> top5 = users.stream()
.sorted(Comparator.comparing(User::getScore).reversed())
.limit(5)
.collect(Collectors.toList());
7.5 求最大值/最小值对象
// 年龄最大的用户
Optional<User> oldest = users.stream()
.max(Comparator.comparing(User::getAge));
// 分数最小的用户
Optional<User> youngest = users.stream()
.min(Comparator.comparing(User::getScore));
7.6 条件统计
// 各部门人数
Map<String, Long> deptCount = users.stream()
.collect(Collectors.groupingBy(User::getDept, Collectors.counting()));
// 各部门平均年龄
Map<String, Double> avgAgeByDept = users.stream()
.collect(Collectors.groupingBy(
User::getDept,
Collectors.averagingInt(User::getAge)
));
7.7 合并多个 List
List<String> allNames = Stream.of(list1, list2, list3)
.flatMap(Collection::stream)
.collect(Collectors.toList());
八、Optional 与空值处理
// 安全获取第一个元素
String first = list.stream()
.filter(s -> s.length() > 3)
.findFirst()
.orElse("默认值");
// 存在就处理
list.stream().findAny()
.ifPresent(System.out::println);
// 抛异常
User user = users.stream()
.filter(u -> u.getId() == 100)
.findFirst()
.orElseThrow(() -> new RuntimeException("用户不存在"));
九、并行流
// 创建并行流
users.parallelStream()
// 串行流 → 并行流
users.stream().parallel()
// 注意:
// 1. 数据量大时才有性能提升
// 2. 注意线程安全问题(避免共享可变状态)
// 3. 有顺序要求时慎用(findFirst vs findAny)
十、常用操作速查表
|
操作 |
类型 |
说明 |
|---|---|---|
|
|
中间 |
条件过滤 |
|
|
中间 |
一对一转换 |
|
|
中间 |
一对多转换+扁平化 |
|
|
中间 |
去重 |
|
|
中间 |
排序 |
|
|
中间 |
取前 n 个 |
|
|
中间 |
跳过前 n 个 |
|
|
中间 |
调试窥探 |
|
|
终端 |
收集为集合 |
|
|
终端 |
遍历 |
|
|
终端 |
归约聚合 |
|
|
终端 |
计数 |
|
|
终端 |
短路匹配 |
|
|
终端 |
查找元素 |
|
|
终端 |
最大/最小值 |
记忆口诀:filter 过滤 map 转,flatMap 打平 sorted 排,distinct 去重 limit 截,skip 跳过 collect 收。