文章目录
- 一、什么是 Java Stream?
- 二、什么时候该用 Stream?(非常重要)
- 数据源(Source)
- 场景 1:对集合进行“流水线式”处理
- 场景 2:需要复杂的分组、统计、聚合
- 场景 3:对代码“表达力”要求高
- 三、什么时候不该用 Stream?
- 场景 1:简单 for 循环反而更清晰
- 场景 2:需要频繁 break / continue / return
- 场景 3:对性能极度敏感的热点代码
- 四、Java Stream 怎么用?(核心 API 总结)
- ①创建 Stream
- ②中间操作(Intermediate Operations)
- ③ 终止操作(Terminal Operations)
- ④Collectors 常见用法
- 五、Stream 使用最佳实践
- 建议 1:保持 Lambda 简短
- 建议 2:不要在 Stream 中修改外部变量
- 建议 3:合理使用 parallelStream
一、什么是 Java Stream?
作为 Java 8 引入的重量级特性,Stream API在日常开发中被频繁提及:
“用 Stream 写代码更优雅”
“Stream 性能是不是更差?”
“什么时候该用 Stream,什么时候不该用?”
Stream 不是集合,也不是数据结构,而是:
对数据源(Collection、Array、IO、Generator 等)进行声明式、函数式处理的一种方式。
它有三个核心特征:
- 不存储数据:Stream 只是对数据的“视图”
- 惰性执行:只有遇到终止操作才真正执行
- 一次性消费:一个 Stream 只能使用一次
List<Integer>list=List.of(1,2,3,4,5);list.stream().filter(i->i>3).map(i->i*2).forEach(System.out::println);这段代码描述的是:
做什么(what),而不是怎么做(how)
二、什么时候该用 Stream?(非常重要)
数据源(Source)
Stream 的数据源可以来自:
- Collection(最常见)
- Array
- Map(实际上是 entry / key / value)
- I/O Channel
- Generator / Supplier
场景 1:对集合进行“流水线式”处理
当你的代码存在大量:
- 遍历
- 过滤
- 映射
- 分组
- 聚合
👉强烈推荐使用 Stream
传统写法:
List<String>result=newArrayList<>();for(Useruser:users){if(user.getAge()>18){result.add(user.getName());}}Stream 写法:
List<String>result=users.stream().filter(u->u.getAge()>18).map(User::getName).toList();✔ 可读性更强
✔ 业务意图更清晰
✔ 减少样板代码
场景 2:需要复杂的分组、统计、聚合
例如:
- 按字段分组
- 求和 / 平均值 / 最大最小值
- 转 Map
Map<String,List<User>>groupByDept=users.stream().collect(Collectors.groupingBy(User::getDept));doubleavgAge=users.stream().collect(Collectors.averagingInt(User::getAge));如果你用 for 循环写这些逻辑,代码通常会又长又容易出错。关于其中Collect的使用,可参考【Java】Java Stream 中的 collect() 方法详解:流最终操作的核心工具_java steam collect方法-CSDN博客
场景 3:对代码“表达力”要求高
Stream 非常适合:
- 业务规则明确
- 操作步骤固定
- 希望一眼看出业务含义
booleanhasInvalidOrder=orders.stream().anyMatch(o->o.getAmount()<=0);这种代码,几乎就是自然语言。
三、什么时候不该用 Stream?
场景 1:简单 for 循环反而更清晰
for(inti=0;i<10;i++){sum+=i;}❌ 不要为了“炫技”改成 Stream
场景 2:需要频繁 break / continue / return
Stream不擅长流程控制:
// 很别扭,不推荐users.stream().forEach(u->{if(u==null)return;});如果逻辑强依赖中断流程,用 for 循环更自然。
场景 3:对性能极度敏感的热点代码
Stream 本质上:
- 创建对象多
- Lambda 有一定开销
在百万级循环 + 高频调用的核心路径中,普通 for 循环通常更快。
结论:
- 业务代码:优先 Stream
- 底层/极限性能:优先 for
四、Java Stream 怎么用?(核心 API 总结)
下边是很容易记的公式
Stream = 数据源 + 对元素的操作规则 + 终止触发
Stream 操作的是“元素”,不是“容器”
①创建 Stream
list.stream();Arrays.stream(arr);Stream.of(1,2,3);②中间操作(Intermediate Operations)
| 方法 | 作用 |
|---|---|
| filter | 过滤 |
| map | 映射 |
| flatMap | 扁平化 |
| distinct | 去重 |
| sorted | 排序 |
| limit / skip | 截取 |
stream.filter(...).map(...)⚠ 中间操作都是惰性的
③ 终止操作(Terminal Operations)
| 方法 | 作用 |
|---|---|
| forEach | 遍历 |
| collect | 收集 |
| count | 数量 |
| anyMatch / allMatch | 匹配 |
| findFirst | 查找 |
List<String>list=stream.collect(Collectors.toList());④Collectors 常见用法
Collectors.toList();Collectors.toMap();Collectors.groupingBy();Collectors.joining(",");五、Stream 使用最佳实践
建议 1:保持 Lambda 简短
// 好.filter(u->u.getAge()>18)// 差(可读性差).filter(u->{// 一堆逻辑})复杂逻辑请抽方法。
建议 2:不要在 Stream 中修改外部变量
// ❌ 不推荐int[]sum={0};list.stream().forEach(i->sum[0]+=i);Stream 更适合无副作用操作。
建议 3:合理使用 parallelStream
list.parallelStream()适合:
- 大数据量
- CPU 密集型
- 无共享状态
不适合:
- IO 操作
- 小数据量