我有一个想法,我会使用条件运算符将我的一些if块变成单行。但是,我想知道是否会有速度差异。我运行了以下测试:
static long startTime;
static long elapsedTime;
static String s;
public static void main(String[] args) {
startTime = System.nanoTime();
s = "";
for (int i= 0; i < 1000000000; i++) {
if (s.equals("")) {
s = "";
}
}
elapsedTime = System.nanoTime() - startTime;
System.out.println("Type 1 took this long: " + elapsedTime + " ns");
startTime = System.nanoTime();
s = "";
for (int i= 0; i < 1000000000; i++) {
s = (s.equals("") ? "" : s);
}
elapsedTime = System.nanoTime() - startTime;
System.out.println("Type 2 took this long: " + elapsedTime + " ns");
}
这是我的结果:
类型 1 花了这么长时间:3293937157 ns
类型 2 花了这么长时间:2856769127 ns
我在这里做错了什么吗?
假设s.equals("")
一定是正确的,这是使代码更快的可行方法吗?
, is this a viable way to make your code faster?
如果您的String s;
是非静态字段,您甚至可以使其更快。静态场比非静态场慢,当你referencing
十亿次时
public static void main(String[] args) {
startTime = System.nanoTime();
String s = "";
.
.
}
编辑:
为什么更快??
这是由于字符串对静态字段的引用。
你可以在它的字节码中看到它
0: ldc #23 // String
2: putstatic #25 // Field s:Ljava/lang/String;
5: iconst_0
6: istore_1
7: goto 22
10: getstatic #25 // Field s:Ljava/lang/String;
13: ldc #23 // String
15: invokevirtual #27 // Method java/lang/String.equals:(L
java/lang/Object;)Z
18: pop
19: iinc 1, 1
22: iload_1
23: ldc #33 // int 1000000000
25: if_icmplt 10
28: return
如您所见getStatic
和putStatic
将被调用十亿次,它的作用是调用静态字段的引用并使用 putStatic 放置字符串的引用
getStatic - 获取类的静态字段值,其中该字段由常量池索引中的字段引用标识(index1 <<8 + index2(
putStatic - 将静态字段设置为类中的值,其中字段由常量池中的字段引用索引标识(索引字节 1 <<8 + 索引字节 2(
查看那些导致程序缓慢的位移
位此外,如果您使用的是global/member field
它将创建相同的字节码,但相反,它将使用 getfield
和putfield
与静态的getStatic
和putStatic
相同
现在让我们看看non static field
字节码
0: ldc #21 // String
2: astore_1
3: iconst_0
4: istore_2
5: goto 23
8: aload_1
9: ldc #21 // String
11: invokevirtual #23 // Method java/lang/String.equals:(L
java/lang/Object;)Z
14: ifeq 20
17: ldc #21 // String
19: astore_1
20: iinc 2, 1
23: iload_2
24: ldc #29 // int 1000000000
26: if_icmplt 8
29: return
如您所见,它仅使用 astore_1
和 aload_1
来保存和加载非静态字段的引用,而无需额外操作。
这对我来说确实闻起来像是过早的优化。如果您仍然打算以这种方式对这两种实现进行微基准测试,我建议您改用isEmpty()
,因为与equals()
相比,底层代码更直接。我的意思是编译器/JVM将为您进行的任何优化都不太可能被equals()
中发生的事情触发,并且更多地反映一个实现相对于另一个实现的任何微小好处,假设这真的很重要。
可读性应该是您决定是要使用if-else
还是? :
的更好规则。
其他答案具有相关的有用信息,但没有一个解决第一种形式是否比第二种形式更有效的真正问题。
这种基准测试不能提供可靠的结果,因为它没有正确完成:对Java代码进行基准测试的一个重要"经验法则"是提供热身。在这种情况下,第一个循环为第二个循环提供预热。
本答案提供了微基准测试的其他说明以及一些有用的链接。