揭开Stream API性能的真面目
你是不是也听过"Stream慢如蜗牛"的吐槽?在处理订单列表时,有人说用for循环跑1秒,用Stream要3秒?但真相是——数据量决定一切!当数据从1万涨到100万,剧情可能完全反转!
小数据量(<1万)时:Stream串行流确实比for循环慢约2倍大数据量(>100万)时:Stream并行流反而快2-4倍!
秘诀:Stream的性能优势需要"大数据+多核CPU"双buff加持!单核环境下并行流反而更慢哦~
实测数据告诉你:Stream到底什么时候更快
2.1 基本类型迭代:小数据慢,大数据快!
遍历int数组找最小值时,这个现象特别明显:
- 100万数据:for循环完虐Stream串行流(2倍差距)
- 1亿数据+12核CPU:Stream并行流反超!仅需for循环50%时间
java
// 小数据量推荐(<10万)
int min = Integer.MAX_VALUE;
for (int num : array) {
if (num < min) min = num;
}
// 大数据量推荐(>100万)
int min = Arrays.stream(array).parallel().min().getAsInt();
2.2 对象操作:越复杂越给力!
处理订单这类复杂对象时,Stream优势更明显:
4000万订单统计用户消费总额:
- for循环:1.00秒(基准)
- Stream串行:0.74秒(提速26%)
- Stream并行:0.18秒(提速5.6倍!)
这就是为什么阿里P8大佬说:"复杂业务逻辑优先用Stream"!
99%的性能问题都是"用错了"!
Stream被重复消费!爆异常!
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
stream.forEach(System.out::println); // 抛出IllegalStateException!
正确做法:每次都创建新Stream
list.stream().forEach(System.out::println);
list.stream().forEach(System.out::println);
并行流往ArrayList里塞数据!丢数据!
List<Integer> list = new ArrayList<>();
IntStream.range(0, 1000).parallel().forEach(list::add); // 结果可能<1000
正确做法:用collect收集
List<Integer> list = IntStream.range(0, 1000)
.parallel()
.boxed()
.collect(Collectors.toList());
5个实战技巧,让Stream性能翻倍!
1 用原始类型流(IntStream/LongStream)避免装箱!
// 低效(频繁装箱)
Stream<Integer> stream = IntStream.range(0, 1000).boxed();
int sum = stream.map(i -> i * 2).sum();
// 高效
int sum = IntStream.range(0, 1000).map(i -> i * 2).sum();
2 filter放前面!先筛后处理!
// 低效(先转换后过滤)
list.stream()
.map(User::getName)
.filter(name -> name.length() > 3)
.collect(Collectors.toList());
// 高效(先过滤后转换)
list.stream()
.filter(user -> user.getName().length() > 3)
.map(User::getName)
.collect(Collectors.toList());
3 并行流要看数据量!
// 小数据用并行(100条数据反而慢3倍)
List<Order> smallOrders = getSmallOrderList(); // 100条数据
smallOrders.parallelStream().forEach(...); // 线程开销 > 计算收益
// 大数据用并行!(100万数据快2.4倍)
List<Order> largeOrders = getLargeOrderList(); // 100万条数据
largeOrders.parallelStream().forEach(...); // 充分利用多核CPU
4 别在Stream里改外部变量!
// 有副作用
List<String> result = new ArrayList<>();
stream.forEach(result::add);
// 纯函数式
List<String> result = stream.collect(Collectors.toList());
5 用短路操作(findFirst/anyMatch)早终止!
// 低效
stream.filter(...).limit(1).findAny();
// 高效
stream.filter(...).findFirst();
到底该用Stream还是for循环?一句话总结!
场景推荐选择性能优势小数据(<10万)+简单操作for循环快10-15%大数据(>100万)+复杂操作Stream并行流快2-5倍代码可读性优先Stream顺序流优雅简洁
记住:没有最好的工具,只有最合适的场景!建议用JMH基准测试验证你的选择~
你在项目中踩过Stream的坑吗?评论区聊聊你的经历吧!