一个lambda表达式是否每次执行时都在堆上创建一个对象?

一个lambda表达式是否每次执行时都在堆上创建一个对象?

当我使用Java 8的新语法糖遍历集合时,如

myStream.forEach(item -> {
  // do something useful});

这不等同于下面的“旧语法”片段吗?

myStream.forEach(new Consumer<Item>() {
  @Override
  public void accept(Item item) {
    // do something useful
  }});

这是否意味着一个新的匿名者Consumer对象是在堆上每次迭代集合时创建的?这需要多少堆空间?它对性能有什么影响?这是否意味着我应该在大型多级数据结构上迭代时使用旧的循环样式?


森栏
浏览 1588回答 3
3回答

芜湖不芜

它是等同的,但不是完全相同的。简单地说,如果lambda表达式没有捕获值,那么它将是在每次调用中重复使用的单例。未精确指定行为。JVM有很大的自由如何实现它。目前,Oracle的JVM为每个lambda表达式创建(至少)一个实例(即在不同的相同表达式之间不共享实例),而是为所有不捕获值的表达式创建单个实例。你可以读这个答案更多细节。在那里,我不仅给出了更详细的描述,而且还测试了代码来观察当前的行为。Java语言规范第一章介绍了这一点15.27.4Lambda表达式的运行时计算”摘要:这些规则旨在为Java编程语言的实现提供灵活性,因为:不需要为每个评估分配一个新对象。由不同的lambda表达式生成的对象不需要属于不同的类(例如,如果主体是相同的)。由计算产生的每个对象不一定属于同一个类(例如,捕获的局部变量可能是内联的)。如果“现有实例”可用,则不需要在以前的lambda计算中创建它(例如,它可能是在封闭类初始化期间分配的)。

侃侃无极

将一个新实例传递给forEach方法。每次这样做,您都会创建一个新对象,而不是为每个循环迭代创建一个对象。迭代在内部完成forEach方法使用相同的“回调”对象实例,直到循环完成为止。因此,循环使用的内存不取决于集合的大小。这不等同于“旧语法”片段吗?是。它在一个很低的水平上有微小的差异,但我认为你不应该关心它们。Lamba表达式使用InvokeDynamic特性而不是匿名类。
打开App,查看更多内容
随时随地看视频慕课网APP