51

我写了两种方法来检查那里的性能

 public class Test1 {

 private String value;

 public void notNull(){
  if( value != null) {
    //do something
  }
}

public void nullNot(){
 if( null != value) {
  //do something
 }
}

}

并在编译后检查它的字节码

public void notNull();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: getfield #2; //Field value:Ljava/lang/String;
4: ifnull 7
7: return
LineNumberTable: 
line 6: 0
line 9: 7

StackMapTable: number_of_entries = 1
frame_type = 7 /* same */


public void nullNot();
Code:
Stack=2, Locals=1, Args_size=1
0: aconst_null
1: aload_0
2: getfield #2; //Field value:Ljava/lang/String;
5: if_acmpeq 8
8: return
LineNumberTable: 
line 12: 0
line 15: 8

StackMapTable: number_of_entries = 1
frame_type = 8 /* same */


}

这里使用两个操作码来实现 if 条件:在第一种情况下,它使用 ifnull- 检查堆栈的顶部值为 null-,在第二种情况下,它使用 if_acmpeq-检查堆栈中的顶部两个值是否相等-

那么,这会对性能产生影响吗?(这将帮助我证明 null 的第一个实现在性能方面以及在可读性方面都很好:))

4

16 回答 16

81

比较生成的字节码几乎没有意义,因为大多数优化发生在 JIT 编译器的运行时。我猜想在这种情况下,任何一个表达式都同样快。如果有什么不同,那是微不足道的。

这不是您需要担心的事情。寻找全局优化。

于 2010-03-08T00:17:40.210 回答
21

如果速度(或内存/无论如何)增益可以忽略不计,请不要以牺牲可读性为代价进行优化。我认为!=null通常更具可读性,所以使用它。

于 2010-03-08T00:23:11.677 回答
12

对于这样的问题,很难知道 JVM 会多么聪明(尽管答案是“如果可能的话通常非常聪明”,在这种情况下看起来很有可能)。但可以肯定的是,测试它:

class Nullcheck {
  public static class Fooble { }

  Fooble[] foo = {null , new Fooble(), null , null,
                  new Fooble(), null, null, new Fooble() };

  public int testFirst() {
    int sum = 0;
    for (int i=0 ; i<1000000000 ; i++) if (foo[i&0x7] != null) sum++;
    return sum;
  }

  public int testSecond() {
    int sum = 0;
    for (int i=0 ; i<1000000000 ; i++) if (null != foo[i&0x7]) sum++;
    return sum;
  }

  public void run() {
    long t0 = System.nanoTime();
    int s1 = testFirst();
    long t1 = System.nanoTime();
    int s2 = testSecond();
    long t2 = System.nanoTime();
    System.out.printf("Difference=%d; %.3f vs. %.3f ns/loop (diff=%.3f)\n",
      s2-s1,(t1-t0)*1e-9,(t2-t1)*1e-9,(t0+t2-2*t1)*1e-9);
  }

  public static void main(String[] args) {
    Nullcheck me = new Nullcheck();
    for (int i=0 ; i<5 ; i++) me.run();
  }
}

在我的机器上,这会产生:

Difference=0; 2.574 vs. 2.583 ns/loop (diff=0.008)
Difference=0; 2.574 vs. 2.573 ns/loop (diff=-0.001)
Difference=0; 1.584 vs. 1.582 ns/loop (diff=-0.003)
Difference=0; 1.582 vs. 1.584 ns/loop (diff=0.002)
Difference=0; 1.582 vs. 1.582 ns/loop (diff=0.000)

所以答案是:不,根本没有有意义的区别。(并且 JIT 编译器可以在相同数量的重复运行后找到额外的技巧来加快速度。)


更新:上面的代码运行一个临时基准。使用JMH(现在它已经存在了!)是帮助避免(某些)微基准测试陷阱的好方法。上面的代码避免了最严重的陷阱,但它没有给出明确的错误估计,并且忽略了有时很重要的各种其他事情。这些天:使用 JMH!此外,如有疑问,请运行您自己的基准测试。细节有时很重要——对于像这样简单的事情来说并不常见,但如果这对你来说真的很重要,你应该检查一个尽可能接近生产的条件。

于 2010-03-08T03:30:42.957 回答
7

除了在 C 语言中避免意外赋值的来之不易的智慧(倾向于将常量放在二元运算符的左侧)之外,我发现左侧的常量易读,因为它将关键值放在最突出的位置。

通常一个函数体将只使用几个变量,并且通常通过上下文可以看出哪个变量正在检查中。通过把常数放在左边,我们更接近地模仿switchand case:给定这个变量,选择一个匹配的值。看到左边的值,人们关注正在选择的特定条件。

当我扫描

if (var == null)

我把它读成,“我们在var这里检查,我们正在比较它是否相等,反对......啊,null。” 相反,当我扫描

if (null == var)

我想,“我们正在查看一个值是否为空,并且……是的,var我们正在检查。” 这是一种更强烈的认可

if (null != var)

我的眼睛立刻就注意到了。

这种直觉来自习惯的一致性,喜欢读自己写的东西,写自己喜欢读的东西。任何一种方式都可以学习,但这并不客观,因为其他人在这里回答说将变量放在左边更清楚。这取决于首先要最清楚表达的哪个方面。

看到字节码的差异令人着迷。谢谢你分享。

于 2010-03-08T00:48:26.617 回答
3

差异可以忽略不计,所以选择最易读的(!= nullimo)

于 2010-03-08T00:28:00.017 回答
2

为了可读性,我会坚持使用 (value != null)。但是你总是可以使用断言。

于 2010-03-08T00:29:21.170 回答
2

像这样的微小优化是编译器的工作,尤其是在 Java 这样的高级语言中。

虽然严格来说它在这里不相关,但不要过早优化!

于 2010-03-08T00:48:59.867 回答
2

从性能上看,没有明显的区别。

但是,首先编写 null 以捕获拼写错误很有用。

例如,如果你习惯编写这样的代码:

if (obj == null)

可能会误写为:

if (obj = null)

从编译器的角度来看,这很好。

但是,如果您习惯将代码编写为:

if (null == obj)

并写错了:

if (null = obj)

编译器会让你知道你在那一行犯了一个错误。

于 2016-11-30T15:12:25.577 回答
1

放在第一位null似乎会生成一个额外的字节码,但除此之外可能没有性能差异。

就我个人而言,在担心性能之前,我不会担心性能。

我会使用这种notNull()方法,这样如果您忘记!并意外键入null = value.

于 2010-03-08T00:20:47.423 回答
1

哦,如果您要求终极性能,请不要创建额外的类或方法。即使是静态方法也需要一些时间,因为 Java 类加载器需要 JIT 加载它。

因此,每当您需要检查变量是否为空时,您只需通过以下任一方式对其进行测试

if (x == null)

或者

if (null == x)

坦率地说,我认为选择两者之一的性能奖励很容易被引入不必要的方法的开销所抵消。

于 2010-03-08T00:28:55.970 回答
1

您可以在编码期间忽略这些非常微小的优化内容

于 2010-03-08T04:17:45.090 回答
1

如您所见,性能差异非常小。不要担心小事,最好更多地关注算法。显然,可读性是一个因素。

于 2010-03-08T04:25:58.520 回答
0

在 Java-8 中,向类引入了两个额外的方法ObjectsObjects#nonNullObjects#isNull,您可以使用它们来替换null检查。有趣的是,它们都首先使用对象:

public static boolean isNull(Object obj) {
    return obj == null;
}

public static boolean nonNull(Object obj) {
    return obj != null;
}

相应地。我想这意味着这是推荐的方式(至少核心 jdk 开发人员使用了这种方式) Objects source code

于 2018-04-11T11:00:38.730 回答
0

我会使用 Java 8 的“新”特性,我写了几个例子:

import java.util.Optional;

public class SillyExample {

public void processWithValidation(final String sampleStringParameter){
    final String sampleString = Optional.ofNullable(sampleStringParameter).orElseThrow(() -> new IllegalArgumentException("String must not be null"));

    //Do what you want with sampleString
}


public void processIfPressent(final String sampleStringParameter){
    Optional.ofNullable(sampleStringParameter).ifPresent(sampleString -> {

        //Do what you want with sampleString

    });

}

public void processIfPressentWithFilter(final String sampleStringParameter){
    Optional.ofNullable(sampleStringParameter).filter("hello"::equalsIgnoreCase).ifPresent(sampleString -> {

        //Do what you want with sampleString

    });

}

}

于 2018-07-28T20:05:53.063 回答
0

我更喜欢null != object它,因为它清楚地表明它只是用于空检查。

于 2019-04-06T10:21:19.130 回答
-3

字节码只是源代码的简单翻译。

于 2010-03-08T07:14:32.160 回答