有人告诉我这样做的速度更快
if(null == object)
vs做这个
if(object == null)
我认为没有区别。请确认效率/性能是否相同。
注意......我的朋友已经进行了性能测试,以证明第一个稍快一点。
更新为什么有些人这样做: 假设您意外地使用“=”而不是预期的等式检查(例如“==”)进行赋值,如果首先使用null,则第一个将给出编译器错误。如此重要,它更好。
答案 0 :(得分:6)
我写了一个程序来测试这个。它只是重复调用两种方法。这些方法的唯一内容是我们要测试性能的两个比较。
public class StackOverflow {
public static void main(String[] args) {
String s = "";
while (true) {
a(s);
b(s);
}
}
private static void a(String s) {
if (s == null);
}
private static void b(String s) {
if (null == s);
}
}
我将一个分析器附加到我的程序中。以下是执行70秒后的结果:
如您所见,a()
的CPU时间轻了近四倍。这表明比较s == null
确实比null == s
更快。
答案 1 :(得分:6)
我将建立在@James回答之上:
public class TestNull {
public void left(String s) {
if (s == null);
}
public void right(String s) {
if (null == s);
}
}
字节码:
public class TestNull {
....
public void left(java.lang.String);
Code:
0: aload_1
1: ifnonnull 4
4: return
public void right(java.lang.String);
Code:
0: aconst_null
1: aload_1
2: if_acmpne 5
5: return
}
首先是更快(当null
位于右侧时)因为java有一个字节码命令ifnonnull
,它将加载的变量与null进行比较。
第二个因为正在使用另一个命令而变慢:if_acmpne
并且它比较了它看作常量的加载aconst_null
。
显然if_acmpne
慢于ifnonnull
:
if_acmpne
弹出堆栈顶部的两个对象引用 比较它们。如果两个对象引用不相等(即如果 它们引用不同的对象),执行分支到地址 (pc + branchoffset),其中pc是if_acmpne操作码的地址 在字节码和branchoffset中是一个16位有符号整数参数 跟随字节码中的if_acmpne操作码。如果是对象 引用引用相同的对象,下一步继续执行 指令。
ifnonnull
从操作数堆栈中弹出顶部对象引用。如果 对象引用不是特殊的空引用,执行分支 到地址(pc + branchoffset),其中pc是地址 字节码和branchoffset中的ifnonnull操作码是16位有符号的 字节码中ifnonnull操作码后面的整数参数。如果 堆栈上的对象引用为null,继续执行 下一条指令。
因此if (s == null)
必须更快(与问题中所述的OP相反)。
答案 2 :(得分:2)
我正在使用此代码进行测试:
public class TestNull {
public void left(String s) {
if (s == null);
}
public void right(String s) {
if (null == s);
}
}
我用javac 1.8.0_05编译它,然后检查了字节码:
public class TestNull {
....
public void left(java.lang.String);
Code:
0: aload_1
1: ifnonnull 4
4: return
public void right(java.lang.String);
Code:
0: aconst_null
1: aload_1
2: if_acmpne 5
5: return
}
显然,left
只推送并弹出堆栈中的1个变量,而right
推送和弹出2.
我不确定ifnonnull
和if_acmpne
之间的性能差异,但如果没有,那么我想虽然没有太大差异,但“正确”比较应该更慢在堆栈上做更多的工作。
然而,有人可以告诉我为什么编译器不会重写代码吗?
答案 3 :(得分:1)
这两个表达式之间确实没有太大的区别,它们运行速度相同。如果有任何差异,我认为这不值得努力。
人们首先编写非变量操作数的唯一原因是为了避免在C语言中使用赋值运算符的意外(在语法上仍然正确)。那些从这些语言迁移到Java的人可能会继续这种做法。
答案 4 :(得分:1)
这纯粹是一种文体差异。在我的测试中,我得到了第一个测试用例的长期运行,这可以归因于JRE设置;其余的迭代表明它们是相同的。
for( int j = 0; j < 10; j++ )
{
Object a = null;
int i = 0;
long tik = System.nanoTime();
while( i < 500000 )
if( a == null )
i++;
System.out.print( "a:" + (System.nanoTime() - tik) );
Object b = null;
i = 0;
tik = System.nanoTime();
while( i < 500000 )
if( null == b )
i++;
System.out.println( " b:" + (System.nanoTime() - tik) );
}
得到以下结果:
a:826735 b:354261
a:318150 b:314729
a:323472 b:314729
a:314350 b:315869
a:318151 b:316630
a:325753 b:314350
a:316250 b:314350
a:314730 b:321952
a:398733 b:340197
a:328794 b:338676
答案 5 :(得分:0)
应该没有区别。由于许多人使用赋值运算符而不是比较运算符,因此产生了这种表示法,这导致许多难以发现的错误。例如:
if(null = object) {
//compiler throws error that you can't assign null
}
编辑:正如乔恩所指出的,这在其他语言中是可行的。我的猜测是习惯延续到了Java的土地上。
答案 6 :(得分:0)
@Test
public void testNullCheck() throws Exception {
Object o = null;
long i1 = System.nanoTime();
if (null == o) {
}
long t1 = System.nanoTime() - i1;
long i2 = System.nanoTime();
if (o == null) {
}
long t2 = System.nanoTime() - i2;
assertThat(t1).isGreaterThan(t2);
}
我的JVM中的位置:
T2 :116L
T1 :304L
所以是的,至少在实践中存在差异。
编辑:
不,不一样。但鉴于JIT的存在时间可能相同,因此需要进行新的测试,以便将JIT纳入账户。请确认效率/性能是否相同。
答案 7 :(得分:-2)
if(null == object)和if(object == null)两者都相同。它们都具有相同的性能,因此,切换顺序对于这两个表达式并不重要。