哪种形式最快?取决于 JIT——它们可能是等价的。如果有的话,很少有程序会注意到差异。
哪种形式最好?几乎无一例外,那种形式可以让您的程序更具可读性。
但是无论我们是否会注意到,是否有任何实际差异?一起来看看吧!
class ArrayTest {
public int[] withNew() {
int[] arr = new int[4];
return arr;
}
public int[] withInitializer() {
int[] arr = {0, 0, 0, 0};
return arr;
}
}
我们用javap -c ArrayTest拆解这个:
Compiled from "ArrayTest.java"
class ArrayTest {
ArrayTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public int[] withNew();
Code:
0: iconst_4
1: newarray int
3: astore_1
4: aload_1
5: areturn
public int[] withInitializer();
Code:
0: iconst_4
1: newarray int
3: dup
4: iconst_0
5: iconst_0
6: iastore
7: dup
8: iconst_1
9: iconst_0
10: iastore
11: dup
12: iconst_2
13: iconst_0
14: iastore
15: dup
16: iconst_3
17: iconst_0
18: iastore
19: astore_1
20: aload_1
21: areturn
}
不,在这种情况下它们不一样 - 使用初始化形式会导致插槽被单独设置为 0,这有点毫无意义,因为它们已经被数组分配归零。所以,基本上相当于这样:
public int[] withNewAndSettingExplicitly() {
int[] arr = new int[4];
arr[0] = 0;
arr[1] = 0;
arr[2] = 0;
arr[3] = 0;
return arr;
}
虽然编译成另一组字节码,大部分相同但不完全相同:
public int[] withNewAndSettingExplicitly();
Code:
0: iconst_4
1: newarray int
3: astore_1
4: aload_1
5: iconst_0
6: iconst_0
7: iastore
8: aload_1
9: iconst_1
10: iconst_0
11: iastore
12: aload_1
13: iconst_2
14: iconst_0
15: iastore
16: aload_1
17: iconst_3
18: iconst_0
19: iastore
20: aload_1
21: areturn
因此,故事的寓意是:如果您希望将所有元素设置为 0,则使用 new int[size] 生成的字节码会更少(可能更快也可能不会更快),但您还必须输入少(恕我直言,这是一个很大的胜利)。如果您想在分配数组时直接设置数组中的值,请使用代码中看起来最好的值,因为无论您选择哪种形式,生成的代码都几乎相同。
现在,回答您的实际问题:
通过避免调用“new”,Method2 是否运行得更快(或不同)?
正如我们所见,new 只是隐藏在初始化语法后面(查找 newarray 操作码)。顺便说一句,JVM 中的分配非常便宜(分代垃圾收集器具有令人愉快的副作用)。
或者上面的两个实现是等价的吗?
正如我们所见 - 不完全是,但不太可能有人会注意到差异。
在任何一种情况下,Java 编译器或运行时是否可以进行优化以避免为这个短暂的临时缓冲区命中内存分配器的开销?
再一次 - 分配很便宜,所以不用担心。然而,最近的 JVM 有一个名为 escape analysis 的小功能,这可能导致数组被堆栈分配而不是堆分配。