' ...!= null'或“空!= ....”最好的表现?

发布于 2024-08-24 04:46:27 字数 964 浏览 4 评论 0原文

我编写了两个方法来检查性能

 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 的第一个实现在性能方面以及可读性方面都很好:))

I wrote two methods to check there performance

 public class Test1 {

 private String value;

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

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

}

and checked it's byte code after compiling

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 */


}

in here two opcodes are used to implement the if condition: in first case it use ifnull- check top value of stack is null-, and in second case it use if_acmpeq- check top two value are equal in the stack-

so, will this make an effect on performance?
(this will helps me to prove first implementation of null is good in performance wise as well as in the aspect of readability :) )

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

烟雨凡馨 2024-08-31 04:46:27

比较生成的字节码基本上没有意义,因为大多数优化发生在 JIT 编译器的运行时。我猜测在这种情况下,任一表达式都同样快。如果有任何差异,那是可以忽略不计的。

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

Comparing the generated bytecodes is mostly meaningless, since most of the optimization happens in run time with the JIT compiler. I'm going to guess that in this case, either expression is equally fast. If there's any difference, it's negligible.

This is not something that you need to worry about. Look for big picture optimizations.

无妨# 2024-08-31 04:46:27

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

Don't optimize at the expense of readability if the speed (or memory/whatever the case may be) gain will be negligible. I think !=null is generally more readable, so use that.

怼怹恏 2024-08-31 04:46:27

有了这样的问题,很难知道 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!另外,如有疑问,请运行您自己的基准测试。细节有时很重要——对于像这样简单的事情来说并不常见,但如果它对你来说真的很重要,你应该检查尽可能接近生产的条件。

With questions like this, it's hard to know how smart the JVM will be (though the answer is "usually pretty smart if possible" and it looks very possible in this case). But just to be sure, test it:

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();
  }
}

And on my machine this yields:

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)

So the answer is: no, no meaningful difference at all. (And the JIT compiler can find extra tricks to speed each up after the same number of repeat runs.)


Update: The code above runs an ad-hoc benchmark. Using JMH (now that it exists!) is a good way to help avoid (some) microbenchmarking pitfalls. The code above avoids the worst pitfalls but it doesn't give explicit error estimates and ignores various other things that sometimes matter. These days: use JMH! Also, when in doubt, run your own benchmarks. Details sometimes matter — not very often for something as straightforward as this, but if it is really important to you you should check in a condition as close to production as you can manage.

顾冷 2024-08-31 04:46:27

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

通常,函数体仅使用几个变量,并且通常通过上下文可以清楚地看出正在检查哪个变量。通过将常量放在左侧,我们可以更接近地模拟 switchcase:给定 this 变量,选择一个匹配值。看到左侧的值,人们会关注所选择的特定条件。

当我扫描时

if (var == null)

,我将其读为“我们正在检查 var 这里,我们正在比较它的相等性,与......啊,null。”相反,当我扫描时

if (null == var)

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

if (null != var)

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

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

看到字节码的差异是令人着迷的。感谢您分享这一点。

Apart from the hard-earned wisdom of avoiding accidental assignment in C, which favors putting the constant on the left of the binary operator, I find the constant on the left to be more readable because it puts the crucial value in the most prominent position.

Usually a function body will use only a few variables, and it's usually apparent by way of context which variable is under inspection. By putting the constant on the left, we more closely mimic switch and case: given this variable, select a matching value. Seeing the value on the left, one focuses on the particular condition being selected.

When I scan

if (var == null)

I read it as, "We're inspecting var here, and we're comparing it for equality, against ... ah, null." Conversely, when I scan

if (null == var)

I think, "We're seeing if a value is null, and ... yes, it's var we're inspecting." It's an even stronger recognition with

if (null != var)

which my eye just picks up on immediately.

This intuition comes from consistency of habit, preferring to read what one writes, and writing what one prefers to read. One can learn it either way, but it's not objectively true as others have answered here that putting the variable on the left is clearer. It depends on what aspect of the expression one wants to be most clear first.

Seeing the bytecode difference was fascinating. Thanks for sharing that.

只是在用心讲痛 2024-08-31 04:46:27

差异可以忽略不计,所以选择最具可读性的内容(!= null imo)

The difference will be negligable so go with what's most readable (!= null imo)

空城之時有危險 2024-08-31 04:46:27

为了便于阅读,我会坚持使用 (value != null) 。但您始终可以使用断言。

I'd stick with (value != null) for readability. But you can always use Assertions.

暖树树初阳… 2024-08-31 04:46:27

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

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

Minute optimization like that is the job of the compiler, especially in high-level languages like Java.

Although strictly it's not relevant here, don't optimize prematurely!

旧伤还要旧人安 2024-08-31 04:46:27

从性能上来看,并没有太大的区别。

然而,先写入 null 来捕获拼写错误是很有用的。

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

if (obj == null)

可能会被错误地写成:

if (obj = null)

从编译器的角度来看,这没问题。

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

if (null == obj)

并且错误地编写了:

if (null = obj)

编译器会让您知道您在该行中犯了错误。

From the point of view, there is no significant difference in performance.

However, it is useful to write the null first to catch typos errors.

For example, if you are used to write this code:

if (obj == null)

Could be wrote by mistake as:

if (obj = null)

From the point of view of the compiler, this is fine.

However, If you are used to write the code as:

if (null == obj)

and made the mistake to write:

if (null = obj)

the compiler will let you know you made a mistake in that line.

第七度阳光i 2024-08-31 04:46:27

首先放置 null 似乎会生成额外的字节码,但除此之外可能不会有性能差异。

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

我会使用 notNull() 方法,这样如果您忘记 ! 并意外键入 null = value ,就不会引发编译器错误>。

Putting null first seems to generate an extra byte-code, but aside from that there may not be a performance difference.

Personally, I wouldn't worry about performance until its time to worry about performance.

I would use the notNull() approach, just so you don't throw a compiler error if you forget the ! and accidentally type null = value.

梦初启 2024-08-31 04:46:27

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

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

if (x == null)

即可

if (null == x)

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

Oh, if you ask for ultimate performance, don't create additional class or methods. Even static methods would take a bit of time as the Java class loader needs to JIT load it.

So, whenever you need to check if a variable is null, you just test it by either

if (x == null)

or

if (null == x)

Frankly I reckon the performance bonus to pick one of the two is easily offset by the overhead of introducing unnecessary methods.

绝不放开 2024-08-31 04:46:27

您可以在编码过程中忽略这些非常微小的优化内容

You can ignore this very minute optimisation stuff during coding

无边思念无边月 2024-08-31 04:46:27

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

As you can see the performance different is very less. Don't worry about the small things it is always better to focus more on algorithm. And obviously readability is a factor.

水溶 2024-08-31 04:46:27

我会使用“新”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

    });

}

}

I would use the "new" Java 8 feature, I write several examples:

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

    });

}

}

好久不见√ 2024-08-31 04:46:27

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

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

并且

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

相应地。我猜这意味着这是推荐的方式(至少核心jdk开发人员使用了这种方式)
对象源代码

In Java-8 two additional methods were introduced to Objects class:
Objects#nonNull and Objects#isNull, which you can use to replace null checks. An interesting things is that both of them use objects first:

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

and

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

correspondingly. I guess it means that this is the recommended way (at least core jdk developers used that approach)
Objects source code

追我者格杀勿论 2024-08-31 04:46:27

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

I would prefer null != object as it makes clearly visible that it's just for null check.

关于从前 2024-08-31 04:46:27

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

Byte code is just a simple translation of the source code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文