Java 编译器是否将循环中的“Set.of”、“List.of”和“Map.of”的临时实例优化为常量

tww*_*wwt 5 java optimization javacompiler

Set给定以下代码片段,其中创建了a 的临时实例,今天的 Java 编译器是否会发现它们不需要为每个循环传递创建实例并优化set为一种最终常量,以便该实例与整个循环?该问题同样适用于whileanddo while循环。

for (/* a loop that iterates quite often */)
{
   var set = Set.of("foo", "blah", "baz");
   // Do something with the set.
}
Run Code Online (Sandbox Code Playgroud)

我还感兴趣是否已经在编译时完成了这样的优化(意味着字节码已经被优化),或者是否存在本质上实现相同效果的运行时优化(由即时编译器进行)。

如果编译器没有看到这一点,唯一的选择似乎是实例化set循环外部以获得最佳效果:

var set = Set.of("foo", "blah", "baz");
for (/* a loop that iterates quite often */)
{
   // Do something with the set.
}
Run Code Online (Sandbox Code Playgroud)

旁注,对于那些发现第一个片段相当糟糕的做法并且无论如何都会像第二个片段一样编写它的人:这个片段实际上是我的真实用例的简化变体,是为了问题的简单性而制作的。让我想到这个问题的真实的、稍微复杂的用例如下,其中需要检查一个字符串是否是极少数字符串之一:

for (/* a loop that iterates quite often */)
{

   // ...

   var testString = // some String to check ...
   if (Set.of("foo", "blah", "baz").contains(testString))
   {
      // Do something.
   }

   // ...

}
Run Code Online (Sandbox Code Playgroud)

假设if条件被附加代码包围(即循环体相当大),我认为人们想要将集合声明为内联而不是远离循环之外。

Ste*_*n C 4

答案不可避免地取决于您使用的 Java 工具。

字节码编译器javac(故意)很幼稚,我不希望它对此进行任何优化。JVM 的 JIT 编译器可能会对此进行优化,但这取决于:

  • 你的Java版本,
  • 使用的 JIT 编译器层
  • 实际需要什么/* do something with the set */

如果这对你来说真的很重要,我会建议几种方法。

  1. 自己重写代码以将声明提升Set到循环之外。IMO,这种方法没有什么根本性的错误,尽管它可能是一种不成熟的优化。(在你给出的例子中,我可能会自己这样做。我只是“感觉正确”。)

  2. 使用 JVM 选项转储 JIT 编译器生成的本机代码并查看它实际执行的操作。但请注意,结果可能会有所不同:见上文。

但即使您非常确定编译器将如何执行,也不清楚您是否应该总体上担心这一点。


请注意,查看字节码javap -c会告诉您java编译器是否正在进行任何优化。(或更可能的是,确认事实并非如此。)