java stream.peek()如何影响字节码?

java stream.peek()如何影响字节码?,java,performance,java-8,java-stream,bytecode,Java,Performance,Java 8,Java Stream,Bytecode,据我所知,如果我有一个带有两个过滤器的流,它们将在字节码中与&&组合 比如说 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)

据我所知,如果我有一个带有两个过滤器的流,它们将在字节码中与&&组合

比如说

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

我的假设是,这一定意味着由于peek调用,操作以某种方式被分离。差不多

if(i%2==0)
  peek
  if(i%3==0)

这是真的吗?如果是真的,它会影响性能(我想不会)。

它根本没有在字节码级别进行优化。每个lambda是一个单独的方法。Java依靠JVM在运行时透明地优化一切。

正如您自己所见,
API是一个透明的API。接收任意的
谓词
实例,可以通过lambda表达式或普通的
(或
枚举
来命名所有可能性)

如果您随后两次调用
filter
,则底层实现可以通过调用将它们连接到单个筛选器,但对于通过lambda表达式实现的谓词,无论是否调用,都不会产生任何后果

与自定义
谓词
实现不同,自定义
谓词
实现可以覆盖
方法,并在识别第二个
谓词
实现时提供优化的内容,为lambda表达式生成的类不会覆盖任何
默认
方法,但是只有一个
abstract
函数方法,这里是
Predicate.test
,因此在本例中,调用
将得到
default
方法返回的内容,一个新的
谓词
,它包含对两个源谓词的引用并将它们组合在一起,很像一个不使用
谓词的流实现,
也可以

因此,这些可能的实现之间没有实质性的区别,如果您插入另一个动作,比如传递给
peek
Consumer
,则没有实质性的区别。当然,它现在比不执行此操作做得更多,因此它对性能有影响,但与谓词无关

但您普遍的误解似乎是您认为以下两者之间存在重大差异:

for(int i=1; i<10; i++) {
    if(i%2==0 && i%3==0)
        System.out.print(i);
}
正如您所看到的,print语句的插入会导致print语句的插入,不会更多,也不会更少。或者,换句话说,
&&
操作符不是一种神奇的融合,它不同于两个嵌套的
if
语句。两者在语义和字节码上都完全相同


同样的情况也适用于流API的使用,尽管如此,代码将更加复杂,因为条件表达式表示为
谓词
实例,而插入的语句是
消费者
s。但在最好的情况下,热点优化器将为流变量生成与循环变量完全相同的优化本机代码。

我认为lambda编译为合成方法,而不是类(参考“为lambda表达式生成的类”)。哪个是正确的?谢谢你详细的回答,这正是我想知道的。我期望两者的表现相同,我只是不确定我的假设是否正确。看起来是这样,但现在我对原因有了更深的理解。谢谢大家!@erickson:lambda表达式被编译成合成方法,JRE将为其生成一个类,该类实现函数接口并调用该合成方法。生成的类的许多属性都是故意未指定的,但它们不重写
默认
方法的事实是:“该类不重写目标函数接口类型或上述其他接口类型的其他方法,尽管它可能重写
对象
类的方法”好的,这是有道理的。我意识到必须有一个对象来实现接口,但我不确定是否有某种反射/动态代理实现,或者在编译时是否有一个合成类。
for(int i=1; i<10; i++) {
    if(i%2==0) {
        System.out.print(i);
        if(i%3==0)
            System.out.print(i);
    }
}