当我使用Java 8的新语法糖遍历一个集合时,例如
myStream.forEach(item -> {
// do something useful
});
这难道不等同于下面的“旧语法”片段吗?
myStream.forEach(new Consumer<Item>() {
@Override
public void accept(Item item) {
// do something useful
}
});
这是否意味着每次遍历集合时都会在堆上创建一个新的匿名Consumer对象?这需要多少堆空间?它有什么性能影响?这是否意味着在迭代大型多级数据结构时,我应该使用旧风格的for循环?
When an instance representing the lambda is created sensitively depends on the exact contents of your lambda's body. Namely, the key factor is what the lambda captures from the lexical environment. If it doesn't capture any state which is variable from creation to creation, then an instance will not be created each time the for-each loop is entered. Instead a synthetic method will be generated at compile time and the lambda use site will just receive a singleton object that delegates to that method.
进一步注意,这方面是依赖于实现的,您可以期待HotSpot未来的改进和进步,以提高效率。有一些一般的计划,例如,创建一个轻量级的对象,没有一个完整的对应类,它有足够的信息转发给单个方法。
下面是一篇关于这个主题的好文章:
http://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood
它们是等价的,但不完全相同。简单地说,如果lambda表达式不捕获值,那么它将是在每次调用中重用的单例。
行为没有被精确地指定。JVM在如何实现它方面有很大的自由。目前,Oracle的JVM为每个lambda表达式创建(至少)一个实例(即不会在不同相同的表达式之间共享实例),但为所有不捕获值的表达式创建单例。
你可以阅读这个答案了解更多细节。在那里,我不仅给出了更详细的描述,而且还测试了代码来观察当前的行为。
这在Java®语言规范的15.27.4章中有介绍。Lambda表达式的运行时求值"
总结:
These rules are meant to offer flexibility to implementations of the Java programming language, in that:
A new object need not be allocated on every evaluation.
Objects produced by different lambda expressions need not belong to different classes (if the bodies are identical, for example).
Every object produced by evaluation need not belong to the same class (captured local variables might be inlined, for example).
If an "existing instance" is available, it need not have been created at a previous lambda evaluation (it might have been allocated during the enclosing class's initialization, for example).