Java 8 Stream anyMatch()为何遍历全流?Optional场景下的性能疑问
这个问题问得特别接地气!我刚用Stream API的时候也踩过一模一样的坑,咱们把这个问题拆解开来看:
问题根源:创建流时的立即求值
你写的Stream.of(abc(), def(), ghi())这一行,其实在创建流的瞬间就已经把三个方法全调用完了!
Java在处理方法参数的时候,会先把所有参数的表达式计算出结果,再把结果传给方法。所以abc()、def()、ghi()这三个方法会在Stream.of()执行前就全部执行,它们的返回值(三个Optional对象)才会被放进流里。这时候后面的anyMatch()只是在遍历已经生成的三个Optional对象,自然不存在“短路”一说——因为方法早就跑完了。
而命令式写法里的||是短路逻辑运算符:只要第一个条件abc().get() >5成立,Java就会直接跳过后面两个方法的调用,根本不会去计算def().get()和ghi().get(),这就是两者的核心区别。
正确的短路写法:用Supplier实现延迟求值
要让anyMatch()实现和||一样的短路效果,我们需要把方法调用变成延迟执行的——也就是只有当Stream需要的时候才去调用方法。这时候可以用Supplier<Optional<Integer>>来包装每个方法:
// 用方法引用包装成Supplier,此时不会立即执行方法 if (Stream.of(this::abc, this::def, this::ghi) .anyMatch(supplier -> supplier.get().get() > 5)) { // 满足条件后的逻辑 }
这样写的话:
- 流里存放的是三个Supplier对象,而不是方法的返回值,创建流的时候不会调用abc/def/ghi方法
anyMatch()遍历流的时候,会逐个调用supplier.get()来执行对应的方法- 一旦找到第一个
supplier.get().get() >5的结果,就会立即停止遍历,后面的Supplier不会再被调用,完美实现短路!
关于性能问题
如果你的abc/def/ghi方法是耗时操作(比如数据库查询、远程调用),那原来的写法确实会带来性能浪费——不管有没有必要,三个方法都要跑一遍。而用Supplier包装后的写法,和命令式逻辑完全一致,只会执行到第一个满足条件的方法为止,不会有多余的性能损耗。
同理,noneMatch()的问题也是一样的:原来的写法会先调用所有方法,改用Supplier包装后,就能实现短路,一旦找到不符合条件的元素就停止遍历。
内容的提问来源于stack exchange,提问作者pvpkiran

