java—检查对象是否为空的最快方法

java—检查对象是否为空的最快方法,java,performance,optimization,Java,Performance,Optimization,有人告诉我这样做更快 if(null == object) vs做这件事 if(object == null) 我认为没有区别。请确认效率/性能相同 注意。。。我的朋友已经做了一个性能测试来证明第一个稍微快一点 更新为什么有些人会这样做: 假设您意外地使用“=”而不是预期的相等检查(如“=”)进行赋值,如果您首先使用null,则第一个将给出编译器错误。所以从图形上看它更好 这两个表达式之间实际上没有太大的差异,它们运行速度一样快。如果有区别的话,我认为这不值得努力 人们首先编写非变量操作数

有人告诉我这样做更快

if(null == object) 
vs做这件事

if(object == null)
我认为没有区别。请确认效率/性能相同

注意。。。我的朋友已经做了一个性能测试来证明第一个稍微快一点

更新为什么有些人会这样做:
假设您意外地使用“=”而不是预期的相等检查(如“=”)进行赋值,如果您首先使用null,则第一个将给出编译器错误。所以从图形上看它更好

这两个表达式之间实际上没有太大的差异,它们运行速度一样快。如果有区别的话,我认为这不值得努力


人们首先编写非变量操作数的唯一原因是为了避免在像C这样的语言中意外(语法上仍然正确)使用赋值运算符。那些从这些语言迁移到Java的人可能会继续这种做法。

应该没有区别。出现这种符号是因为许多人使用赋值运算符而不是比较运算符,这导致了许多难以发现的bug。例如:

if(null = object) {
    //compiler throws error that you can't assign null
}
编辑:正如乔恩指出的,这在其他语言中是可能的。我的猜测是,这种习惯一直延续到爪哇岛

@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应该被考虑到帐户中。

我编写了一个程序来测试这一点。它所做的只是重复调用两个方法。这些方法的唯一内容是我们想要测试其性能的两个比较

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
快,如果(null==object)和if(object==null)都相同。它们都具有相同的性能,因此,切换顺序对于这两个表达式并不重要。

我正在使用以下代码进行测试:

public class TestNull {
   public void left(String s) {
       if (s == null);
   }
   public void right(String s) {
       if (null == s);
   }
}
我用JavaC1.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
之间的性能差异,但是如果没有,那么我想虽然没有太大的差异,“正确”的比较应该会更慢,以便在堆栈上做更多的工作


但是,有人能告诉我为什么编译器不重写代码吗

这纯粹是风格上的差异。在我的测试中,我得到了第一个测试用例的长时间运行,这可以归因于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) );
        }

我将建立在@James answer之上:

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\u acmpne
,它比较加载的
acost\u null
,它将其视为常量

显然
如果acmpne
ifnonnull
慢:

if_acmpne
将顶部的两个对象引用从堆栈中弹出,然后 比较它们。如果两个对象引用不相等(即如果 它们引用不同的对象),执行分支到地址 (pc+branchoffset),其中pc是if_acmpne操作码的地址 在字节码和branchoffset中,是一个16位有符号整数参数 在字节码中的if_acmpne操作码之后。如果对象 引用引用同一个对象,执行将在下一个 指示

ifnonnull
从操作数堆栈中弹出顶部对象引用。如果 对象引用不是特殊的空引用,执行分支 至地址(pc+branchoffset),其中pc是 字节码和branchoffset中的ifnonnull操作码是16位有符号的 字节码中ifnonnull操作码后面的整数参数。如果 堆栈上的对象引用为null,将在 下一个指令


因此
如果(s==null)
必须更快(与问题中OP所述相反)。

您如何通过编写一个测试程序来确认差异并计时?或者看看生成的字节码。也许这个问题可以推广到其他有趣的作业或比较。我不明白为什么我会投反对票。我运行了Chechus的测试,我确实看到了差异,尽管这不会影响性能。那是我唯一的问题。如果我有三千张这样的支票呢?那么我想知道影响。这就是我想知道的。@OldProgrammer这些优化不是在运行时发生的吗?有什么理由要查找字节码吗?我在程序上附加了一个分析器来测试这个。看起来OP听到的谣言是真的,至少在我的机器上是这样。我无法将结果放入评论中,所以我发布了自己的答案。如果有人能解释我得到的结果,请随意在你自己的答案中使用我的结果,然后让我知道,这样我就可以删除我的结果。这个符号是由于人们在其他语言中犯作业错误而产生的
如果(object=null){
在Java中也是无效的…@JonSkeet-True,我认为人们的习惯只是延续到了Java中。与我的测试相比,JIT似乎发挥了作用并进行了优化,因为我的测试始终与此结果相反(见下面我的答案)!使用与Oracle 1.7 JRE相同的代码,并在该代码运行一段时间后附加探查器,I g
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        
}