Chi*_*pen 2 java performance bytecode java-8 java-stream
据我所知,如果我有一个带有两个过滤器的流,它们将与字节码中的&&结合使用.
例如
IntStream.range(1,10)
.filter(i -> i % 2 == 0)
.filter(i -> i % 3 == 0)
.sum();
Run Code Online (Sandbox Code Playgroud)
会像i%2 == 0 && i%3 == 0.
偷看会影响这个吗?
如果你在第一个文件管理器后偷看你得到2468,如果你偷看第二个,你只得到6(当然).
但如果你偷看这两个地方
IntStream.range(1,10)
.filter(integer -> integer % 2 == 0)
.peek(i-> System.out.print(i))
.filter(integer -> integer % 3 == 0)
.peek(i-> System.out.print(i))
.sum();
Run Code Online (Sandbox Code Playgroud)
你得到24668.
我的假设是,这必然意味着操作在某种程度上是由于窥视调用而分开的.就像是
if(i%2==0)
peek
if(i%3==0)
Run Code Online (Sandbox Code Playgroud)
这是真的,如果是这样会影响性能(我认为不会).
该Stream
API是一个普通的Java API,你可以看到自己.它的filter
方法接收一个任意的Predicate
实例,无论是通过lambda表达式还是普通的class
(或enum
命名所有可能性).
如果您filter
随后调用了两次,则底层实现可以通过调用将它们连接到单个过滤器,Predicate.and
但是在通过lambda表达式实现的谓词的情况下它是否有效.
与自定义Predicate
实现不同,自定义实现可以覆盖and
方法并在它们识别第二个Predicate
实现的情况下提供优化的东西,为lambda表达式生成的类不会覆盖任何default
方法,而只是一个abstract
函数方法,这里Predicate.test
,所以在这种情况下,调用and
将得到该default
方法返回的内容,一个新的Predicate
,它包含对两个源谓词的引用并将它们组合起来,就像一个不使用的Stream实现Predicate.and
一样.
因此,这些可能的实现之间没有实质性的差异,如果你插入另一个像Consumer
传递给peek
中间的动作则没有.当然,它现在比没有这个动作比较多,所以它有一个性能的影响,但不是关于谓词.
但是你的一般误解似乎是你认为以下两者之间存在重大差异:
for(int i=1; i<10; i++) {
if(i%2==0 && i%3==0)
System.out.print(i);
}
Run Code Online (Sandbox Code Playgroud)
和
for(int i=1; i<10; i++) {
if(i%2==0) {
System.out.print(i);
if(i%3==0)
System.out.print(i);
}
}
Run Code Online (Sandbox Code Playgroud)
看看编译方法的字节码:
// first variant second variant
0: iconst_1 0: iconst_1
1: istore_1 1: istore_1
2: iload_1 2: iload_1
3: bipush 10 3: bipush 10
5: if_icmpge 33 5: if_icmpge 40
8: iload_1 8: iload_1
9: iconst_2 9: iconst_2
10: irem 10: irem
11: ifne 27 11: ifne 34
14: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
17: iload_1
18: invokevirtual #3 // Method java/io/PrintStream.print:(I)V
14: iload_1 21: iload_1
15: iconst_3 22: iconst_3
16: irem 23: irem
17: ifne 27 24: ifne 34
20: getstatic #2 27: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
23: iload_1 30: iload_1
24: invokevirtual #3 31: invokevirtual #3 // Method java/io/PrintStream.print:(I)V
27: iinc 1, 1 34: iinc 1, 1
30: goto 2 37: goto 2
33: return 40: return
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,插入print语句恰好会导致插入print语句,仅此而已.或者,换句话说,&&
运算符不是与两个嵌套if
语句不同的神奇融合.两者在语义和字节代码中完全相同.
这同样适用于Stream API用法,尽管如此,代码将更复杂,因为条件表达式表示为Predicate
实例,插入的语句是Consumer
s.但在最好的情况下,HotSpot优化器将为Stream变体生成与循环变体完全相同的优化本机代码.