文章目录
- 272. Java Stream API - 使用数字专用流,避免装箱开销
- 🚫 问题:普通 `Stream` 会引发装箱性能问题
- ✅ 解决方案:使用数字专用流
- 📊 IntStream 示例:终端操作更丰富
- 🧮 `summaryStatistics()` 示例
- ✅ 最佳实践:使用 Stream 的注意事项
- ⚠️ 一、不要复用 Stream 对象
- ⚠️ 二、不要把 `Stream` 存成字段或变量长期保留
- ⚠️ 三、避免对流外部变量产生副作用
- ✅ 总结小贴士
272. Java Stream API - 使用数字专用流,避免装箱开销
我们知道,Java中的Stream<T>处理的是对象,但如果你处理的是大量的数字(int、long、double),就需要注意“装箱与拆箱(boxing/unboxing)”的问题。
🚫 问题:普通Stream会引发装箱性能问题
Stream<Integer>numbers=List.of(1,2,3,4).stream();// 每个 int 都是 Integer 对象intsum=numbers.reduce(0,Integer::sum);// 多次装箱与拆箱,性能不佳✅ 解决方案:使用数字专用流
Java提供了三个专用数字流接口来避免装箱开销:
IntStream:处理int值流LongStream:处理long值流DoubleStream:处理double值流
它们使用的是原始类型(primitive types),不会自动装箱,性能更高。
📊 IntStream 示例:终端操作更丰富
importjava.util.stream.IntStream;publicclassStatExample{publicstaticvoidmain(String[]args){IntStreamstream=IntStream.of(10,20,30,40,50);intsum=stream.sum();// 🚀 无装箱,高效计算System.out.println("Sum: "+sum);// 输出:Sum: 150}}数字流接口提供了一些Stream<T>没有的终端操作:
| 方法名 | 描述 |
|---|---|
sum() | 计算总和 |
min(),max() | 查找最小值和最大值 |
average() | 计算平均值(返回OptionalDouble) |
summaryStatistics() | 返回一个统计对象:总数、最小、最大、平均、总和 |
🧮summaryStatistics()示例
IntSummaryStatisticsstats=IntStream.of(10,20,30,40).summaryStatistics();System.out.println("Count: "+stats.getCount());System.out.println("Sum: "+stats.getSum());System.out.println("Average: "+stats.getAverage());System.out.println("Min: "+stats.getMin());System.out.println("Max: "+stats.getMax());输出:
Count:4Sum:100Average:25.0Min:10Max:40🎯 只需遍历一次数据流,五个指标一次到位!
✅ 最佳实践:使用 Stream 的注意事项
使用 Stream 时,以下行为要避免或注意,以确保安全性、可读性和性能。
⚠️ 一、不要复用 Stream 对象
错误示例:
Stream<String>stream=List.of("A","B","C").stream();varupper=stream.map(String::toUpperCase);varlist=stream.toList();// ❌ 第二次使用同一个 stream,会抛异常!正确示例:
List<String>data=List.of("A","B","C");varupper=data.stream().map(String::toUpperCase).toList();varlower=data.stream().map(String::toLowerCase).toList();// ✅ 每次新建 stream📌流只能使用一次,操作完就“关闭”了。
⚠️ 二、不要把Stream存成字段或变量长期保留
为什么?
Stream 是连接到数据源的“流水线”,并不存储数据。如果保存到字段中,容易:
- 被重复使用引发错误
- 引入副作用
- 增加代码难以追踪的数据流动问题
✅ 最佳实践:流应就地创建、就地消费!
// ✅ 好做法List<Integer>result=List.of(1,2,3).stream().map(i->i*2).filter(i->i>3).toList();⚠️ 三、避免对流外部变量产生副作用
int[]total={0};List.of(1,2,3).stream().forEach(i->total[0]+=i);// ❌ 修改外部变量,副作用!System.out.println(total[0]);这段代码虽然能运行,但它:
- 破坏了函数式编程的纯粹性
- 在并行流中(parallel stream)可能导致竞态条件(race condition)
✅ 推荐写法:
intsum=List.of(1,2,3).stream().mapToInt(Integer::intValue).sum();// ✅ 无副作用✅ 总结小贴士
| 规则 | 原因 |
|---|---|
| ✔ 使用 IntStream、LongStream、DoubleStream | 避免装箱,提高数字处理性能 |
| ✔ 每次新建 stream,不要重复使用同一个 | Stream 一次性消费,不能重复使用 |
| ✔ 不要把 stream 存成字段或变量长期保留 | 增加代码复杂性,并可能导致使用错误 |
| ✔ 避免修改外部变量(无副作用) | 保持函数式风格,保证线程安全 |
| ✔ 中间操作返回新 stream,不会立刻执行 | 执行在终端操作触发时才发生 |