我有一个想法,我会使用条件运算符将我的一些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");
}
这是我的结果:
Type 1花了这么长时间:3293937157 ns
类型2花了这么长时间:2856769127 ns
我在这里做错了吗?
假设s.equals("")
必然是正确的,这是一种使代码更快的可行方法吗?
答案 0 :(得分:6)
, 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&lt;&lt;&lt; 8&index2)
putStatic - 将静态字段设置为类中的值,其中字段由常量池中的字段引用索引标识(indexbyte1&lt;&lt;&lt;&lt; 8&indexbyte2)
查看导致程序缓慢的位移
此外,如果您使用的是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
来保存和加载非静态字段的引用而无需额外的操作。
答案 1 :(得分:5)
这听起来像是对我的过早优化。如果您仍打算以这种方式对这两种实现进行微基准测试,我建议使用isEmpty()
,因为与equals()
相比,它的底层代码更简单。通过这种方式,我的意思是编译器/ JVM将为您做的任何优化都不太可能由equals()
中发生的事情触发,更能反映一个实现相对于另一个实现的任何微小优势,假设真的很重要
可读性应该是您决定是否要使用if-else
或? :
的更好规则。
答案 2 :(得分:2)
其他答案包含相关的有用信息,但如果第一个表单比第二个表单更有效,它们都不会解决真正的问题。
这种基准测试不能提供可靠的结果,因为它没有正确完成:在Java代码基准测试中,一个重要的“经验法则”是提供预热。在这种情况下,第一个循环为第二个循环提供预热。
This answer提供了有关微基准测试以及一些有用链接的其他说明。