性能取决于很多因素,很难预测。通常,我们会说,如果您的主管声称存在绩效问题,您的主管负责解释什么问题。
有人可能害怕的一件事是,在幕后,会为每个 lambda 创建站点(使用当前实现)生成一个类,因此如果相关代码只执行一次,这可能会被视为浪费的资源。这与 lambda 表达式作为普通命令式代码具有更高的初始化开销这一事实相一致(我们在这里不与内部类进行比较),因此在只运行一次的类初始化器中,您可以考虑避免它。这也符合您应该never use parallel streams in class initializers 的事实,因此无论如何这里都没有这种潜在优势。
对于可能被 JVM 优化的普通、频繁执行的代码,不会出现这些问题。正如您所猜想的那样,为 lambda 表达式生成的类得到与其他类相同的处理(优化)。在这些地方,对集合调用 forEach 可能会比 for 循环更高效。
为Iterator 或 lambda 表达式创建的临时对象实例可以忽略不计,但是可能值得注意的是,foreach 循环将始终创建一个Iterator 实例,而lambda expression do not always do。尽管Iterable.forEach 的default 实现也会创建Iterator,但一些最常用的集合会借此机会提供专门的实现,尤其是ArrayList。
ArrayList 的forEach 基本上是一个数组上的for 循环,没有任何Iterator。然后它将调用Consumer 的accept 方法,这将是一个生成的类,其中包含对包含您的lambda 表达式代码的合成方法的简单委托。为了优化整个循环,优化器的范围必须跨越数组上的ArrayList 循环(优化器可识别的常见习语),包含琐碎委托的合成accept 方法和包含实际委托的方法代码。
相比之下,当使用 foreach 循环迭代同一个列表时,会创建一个 Iterator 实现,其中包含 ArrayList 迭代逻辑,分布在两个方法上,hasNext() 和 next() 以及 @ 的实例变量987654343@。循环将重复调用 hasNext() 方法来检查结束条件 (index<size) 和 next() 在返回元素之前重新检查条件,因为不保证调用者会这样做在next() 之前正确调用hasNext()。当然,优化器能够消除这种重复,但这比一开始就没有它需要更多的努力。因此,要获得与forEach 方法相同的性能,优化器的范围必须跨越您的循环代码、非平凡的hasNext() 实现和非平凡的next() 实现。
类似的事情也可能适用于具有专门的forEach 实现的其他集合。这也适用于 Stream 操作,如果源提供了专门的 Spliterator 实现,它不会将迭代逻辑分布在两个方法上,例如 Iterator。
因此,如果您想讨论 foreach 与 forEach(…) 的技术方面,您可以使用这些信息。
但如上所述,这些方面仅描述了潜在的性能方面,因为优化器的工作和其他运行时环境方面可能会完全改变结果。我认为,根据经验,循环体/动作越小,forEach 方法就越合适。这与避免过长的 lambda 表达式的准则完美协调。