Java Stream 操作详解:从基础到实战
Java Stream(流)是 Java 8 引入的核心特性,基于函数式编程思想,为集合数据处理提供了简洁、高效的链式操作。Stream 不存储数据,也不修改源集合,而是通过一系列中间操作(惰性求值)和终止操作(触发执行),实现对数据的过滤、转换、聚合等处理。本文将全面解析 Stream 的核心概念、操作方法及最佳实践。
Stream 核心概念
什么是 Stream?
Stream 是 “数据流” 的抽象,可将集合(如 List、Set)转换为流,通过链式操作对元素进行处理。其核心特点:
- 不存储数据:流只是数据的 “视图”,操作不会改变源集合。
- 惰性求值:中间操作仅描述处理逻辑,不实际执行,直到终止操作被调用才一次性执行。
- 一次性使用:流只能被消费一次,执行终止操作后即失效,再次使用需重新创建。
Stream 操作流程
Stream 处理分为三个阶段,形成 “流水线”:
1 | 数据源(集合、数组等)→ 中间操作(过滤、转换等)→ 终止操作(聚合、收集等) |
- 数据源:可是
Collection(通过stream()或parallelStream())、数组(Arrays.stream())、生成器(Stream.generate())等。 - 中间操作:对数据进行处理(如筛选、排序),返回新的流,可链式调用。
- 终止操作:触发流的执行,返回非流结果(如集合、数值、布尔值等)。
Stream 的创建(生成流)
从集合创建
所有 Collection 接口的实现类(如 List、Set)均可通过 stream() 或 parallelStream() 生成流:
1 | List<String> list = Arrays.asList("a", "b", "c"); |
从数组创建
通过 Arrays.stream() 从数组生成流:
1 | String[] arr = {"x", "y", "z"}; |
直接生成流
通过 Stream 静态方法直接生成流:
| 方法 | 功能描述 | 示例 |
|---|---|---|
Stream.of(T...) |
从可变参数生成流 | Stream.of("hello", "world") |
Stream.empty() |
生成空流 | Stream<Object> empty = Stream.empty(); |
Stream.generate(Supplier<T>) |
生成无限流(需配合 limit 限制长度) |
Stream.generate(() -> "echo").limit(5) |
Stream.iterate(T, UnaryOperator<T>) |
生成无限迭代流 | Stream.iterate(1, n -> n + 2).limit(3) // 1,3,5 |
中间操作:描述处理逻辑
中间操作返回新的 Stream,可链式调用,仅在终止操作触发时才执行。常见中间操作如下:
筛选与切片
(1)filter(Predicate<T>):过滤元素
保留满足 Predicate 条件(返回 true)的元素:
1 | List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); |
(2)distinct():去重
基于元素的 equals() 和 hashCode() 去重:
1 | List<String> words = Arrays.asList("a", "a", "b", "c", "c"); |
(3)limit(long n):截断流
保留前 n 个元素:
1 | // 保留前3个元素:1,2,3 |
(4)skip(long n):跳过元素
跳过前 n 个元素(若流长度小于 n,返回空流):
1 | List<Integer> nums = Arrays.asList(1, 2, 3, 4); |
注意:limit 和 skip 顺序不同,结果不同:
1 | // 先limit(2)再skip(1):保留第2个元素(2) |
排序
(1)sorted():自然排序
元素需实现 Comparable 接口(如 Integer、String):
1 | List<String> strs = Arrays.asList("c", "a", "b"); |
(2)sorted(Comparator<T>):定制排序
通过 Comparator 自定义排序逻辑:
1 | List<User> users = Arrays.asList( |
映射:转换元素
(1)map(Function<T, R>):元素转换
将流中元素通过 Function 转换为另一种类型:
1 | List<String> words = Arrays.asList("hello", "world"); |
(2)flatMap(Function<T, Stream<R>>):流合并
将每个元素转换为一个流,再合并为单个流(解决 map 可能产生的 “流中流” 问题):
1 | List<String> list = Arrays.asList("abc", "def"); |
map vs flatMap:
map:T → R(每个元素转换为单个对象)。flatMap:T → Stream<R>(每个元素转换为流,再合并)。
终止操作:触发执行并返回结果
终止操作触发中间操作的执行,并返回非流结果(如集合、数值、布尔值等)。
匹配与查找
(1)allMatch(Predicate<T>):全匹配
检查是否所有元素都满足条件:
1 | List<Integer> nums = Arrays.asList(2, 4, 6); |
(2)anyMatch(Predicate<T>):任一匹配
检查是否至少一个元素满足条件:
1 | List<String> words = Arrays.asList("a", "b", ""); |
(3)noneMatch(Predicate<T>):全不匹配
检查是否所有元素都不满足条件:
1 | List<Integer> nums = Arrays.asList(1, 3, 5); |
(4)findFirst():获取第一个元素
返回流中第一个元素(Optional<T> 类型,避免空指针):
1 | List<String> words = Arrays.asList("a", "b", "c"); |
(5)findAny():获取任一元素
返回流中任意一个元素(并行流中可能非第一个,顺序流中通常是第一个):
1 | List<Integer> nums = Arrays.asList(1, 2, 3); |
(6)count():计数
返回流中元素的数量:
1 | long count = Arrays.asList(1, 2, 3).stream().count(); // 3 |
(7)max(Comparator<T>) / min(Comparator<T>):最值
返回流中按比较器排序后的最大值/ 最小值:
1 | List<Integer> nums = Arrays.asList(3, 1, 2); |
归约(reduce):聚合为单个值
reduce 用于将流中元素通过二元运算聚合为一个值,有两种常用形式:
(1)无初始值:reduce(BinaryOperator<T>)
返回 Optional<T>(流可能为空):
1 | List<Integer> nums = Arrays.asList(1, 2, 3, 4); |
(2)有初始值:reduce(T identity, BinaryOperator<T>)
以 identity 为初始值,返回 T(非 Optional,因初始值保证结果非空):
1 | // 初始值为0,求和:0+1+2+3+4=10 |
注:
count()、max()、min()等方法底层均基于reduce实现。
收集(collect):转换为集合或其他类型
collect(Collector) 是最常用的终止操作,通过 Collectors 工具类提供的收集器,将流转换为 List、Set、Map 等,或进行分组、聚合等操作。
(1)基本收集:转集合
1 | List<String> list = Arrays.asList("a", "b"); |
(2)数据汇总
Collectors 提供了多种汇总工具,适用于数值型元素:
| 方法 | 功能 | 示例 |
|---|---|---|
summingInt(ToIntFunction) |
求和 | summingInt(User::getAge) |
averagingInt(ToIntFunction) |
求平均值 | averagingInt(User::getAge) |
summarizingInt(ToIntFunction) |
汇总(计数、总和、平均值等) | summarizingInt(User::getAge) |
示例:
1 | List<User> users = Arrays.asList( |
(3)字符串连接
joining 用于拼接流中字符串元素:
1 | List<String> words = Arrays.asList("Hello", "World"); |
(4)分组(groupingBy)
将元素按指定条件分组,返回 Map<分组键, List<元素>>:
1 | List<User> users = Arrays.asList( |
(5)分区(partitioningBy)
特殊的分组,按 Predicate 结果分为两组(true 和 false):
1 | List<Integer> nums = Arrays.asList(1, 2, 3, 4); |
Optional:避免空指针的容器
Optional<T> 是 Stream 终止操作的常见返回类型,用于封装可能为 null 的值,避免直接操作 null 导致的 NullPointerException。
核心方法
| 方法 | 功能描述 |
|---|---|
Optional.of(T) |
封装非 null 值(值为 null 则抛异常) |
Optional.ofNullable(T) |
封装可能为 null 的值(安全) |
optional.isPresent() |
判断值是否存在(不推荐,类似 obj != null) |
optional.ifPresent(Consumer<T>) |
若值存在,执行 Consumer 操作 |
optional.orElse(T) |
若值不存在,返回默认值 T |
optional.orElseGet(Supplier<T>) |
若值不存在,通过 Supplier 生成默认值 |
optional.orElseThrow(Supplier<Exception>) |
若值不存在,抛出 Supplier 生成的异常 |
最佳实践
避免使用 isPresent() + get()(与 null 判断无差异),应使用函数式方法:
1 | // 反例:低效且与 null 判断无异 |
v1.3.10