据我所知,如果我有一个带有两个过滤器的流,它们将与&&和在字节码中。
例如
IntStream.range(1,10)
.filter(i -> i % 2 == 0)
.filter(i -> i % 3 == 0)
.sum();
会像i%2 == 0&& i%3 == 0。
peek会影响这个吗?
如果您要查看第一个文件管理器,则会得到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();
你得到24668.
我的假设是,这必然意味着操作在某种程度上是由于窥视调用而分开的。像
这样的东西if(i%2==0)
peek
if(i%3==0)
这是真的,如果是这样会影响性能(我认为不会)。
答案 0 :(得分:6)
Stream
API是ordinary Java API,您可以看到自己。 It’s filter
method接收任意Predicate
个实例,可以通过lambda表达式实现,也可以通过普通class
(或enum
来命名所有可能性)。
如果您随后调用filter
两次,则底层实现可以通过调用Predicate.and
将它们连接到单个过滤器,但无论它是否存在都没有后果通过lambda表达式实现谓词的情况。
与自定义Predicate
实现不同,后者可以覆盖and
方法,并在它们识别第二个Predicate
实现的情况下提供优化的内容,为lambda表达式生成的类不会覆盖任何default
方法,但只有一个abstract
函数方法,Predicate.test
,所以在这种情况下,调用and
将获得default
方法返回的内容,新的Predicate
包含对两个源谓词的引用并将它们组合在一起,就像不使用Predicate.and
的Stream实现一样。
因此,这些可能的实现之间没有实质性的差异,如果您在中间插入另一个传递给Consumer
的{{1}}之类的操作则没有。当然,它现在做的不仅仅是没有这个动作,所以 会影响性能,但不会影响谓词。
但是你的一般误解似乎是你认为:
之间存在重大差异peek
和
for(int i=1; i<10; i++) {
if(i%2==0 && i%3==0)
System.out.print(i);
}
查看已编译方法的字节代码:
for(int i=1; i<10; i++) {
if(i%2==0) {
System.out.print(i);
if(i%3==0)
System.out.print(i);
}
}
正如您所看到的,插入print语句会导致完全插入print语句,仅此而已。或者,换句话说,// 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
运算符不是与两个嵌套&&
语句不同的神奇融合事物。两者在语义和字节代码中完全相同。
这同样适用于Stream API用法,尽管如此,代码会更复杂,因为条件表达式表示为if
个实例,而插入的语句是Predicate
s。但在最好的情况下,HotSpot优化器将为Stream变体生成与循环变体完全相同的优化本机代码。
答案 1 :(得分:0)
在字节码级别根本没有优化。每个lambda是一个单独的方法。 Java依靠JVM在运行时透明地优化所有内容。