我写了两种检查性能的方法
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-检查前两个值在堆栈中相等 - < / p>
那么,这会对性能产生影响吗? (这将有助于我证明null的首次实现在性能方面以及可读性方面都很好:))
答案 0 :(得分:70)
比较生成的字节码大多没有意义,因为大多数优化都是在运行时使用JIT编译器进行的。我猜想在这种情况下,表达式同样快。如果有任何差异,那就可以忽略不计了。
这不是您需要担心的事情。寻找大图优化。
答案 1 :(得分:20)
如果速度(或内存/无论情况如何)增益可以忽略不计,请不要以牺牲可读性为代价进行优化。我认为!=null
通常更具可读性,所以请使用它。
答案 2 :(得分:9)
有了这样的问题,很难知道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编译器可以在相同次数的重复运行之后找到额外的技巧来加速每一次。)
更新:上面的代码运行了一个ad-hoc基准测试。使用JMH(现在它存在!)是一种帮助避免(某些)微基准测试陷阱的好方法。上面的代码避免了最严重的陷阱,但它没有给出明确的错误估计,并忽略了有时重要的其他各种事情。这些天:使用JMH!此外,如有疑问,请运行自己的基准测试。细节有时很重要 - 对于像这样简单的事情来说并不常见,但如果它对你来说非常重要,你应该检查一个尽可能接近生产的条件。
答案 3 :(得分:6)
除了在C中避免意外分配的来之不易的智慧,这有利于将常量放在二元运算符的左边,我发现左边的常量更多可读,因为它把关键价值放在最突出的位置。
通常函数体只会使用少量变量,而且通常通过上下文显而易见的是变量正在检查中。通过将常量放在左侧,我们更接近地模仿switch
和case
:给定此变量,选择匹配值。看到左边的值,一个侧重于选择的特定条件。
我扫描时
if (var == null)
我读到它,“我们在这里检查var
,我们正在比较它是否相等,反对......啊,null。”相反,当我扫描
if (null == var)
我认为,“我们看到一个值是否为空,并且......是的,我们正在检查var
。”
if (null != var)
我的眼睛立即接受了。
这种直觉来自于习惯的一致性,更喜欢阅读所写的内容,以及写出喜欢阅读的内容。人们可以从任何一种方式学习它,但它不是客观真实的,因为其他人在这里回答说将变量放在左边更清楚。这取决于人们希望最清楚的表达方式。
看到字节码差异非常吸引人。谢谢你的分享。
答案 4 :(得分:3)
差别是可以忽略的,所以选择最具可读性(!= null
imo)
答案 5 :(得分:2)
为了便于阅读,我坚持使用(value!= null)。但是你总是可以使用断言。
答案 6 :(得分:2)
像这样的分钟优化是编译器的工作,特别是在像Java这样的高级语言中。
虽然这里严格说来并不重要,但不要过早优化!
答案 7 :(得分:2)
从观点来看,性能没有显着差异。
但是,首先编写null以捕获拼写错误很有用。
例如,如果您习惯于编写此代码:
if (obj == null)
可能被误写为:
if (obj = null)
从编译器的角度来看,这很好。
但是,如果您习惯将代码编写为:
if (null == obj)
写错了:
if (null = obj)
编译器会告诉你你在那行中犯了错误。
答案 8 :(得分:1)
首先放置null
似乎会产生额外的字节码,但除此之外可能没有性能差异。
就个人而言,在担心表现之前,我不会担心表现。
我会使用notNull()
方法,因为如果忘记!
并意外输入null = value
,就不会抛出编译器错误。
答案 9 :(得分:1)
哦,如果您要求最终表现,请不要创建其他类或方法。甚至静态方法也需要一些时间,因为Java类加载器需要JIT加载它。
因此,每当您需要检查变量是否为null时,您只需通过
进行测试if (x == null)
或
if (null == x)
坦率地说,我认为选择其中一个的性能奖励很容易被引入不必要方法的开销所抵消。
答案 10 :(得分:1)
您可以在编码期间忽略这些非常微小的优化内容
答案 11 :(得分:1)
正如您所看到的,性能差异非常小。不要担心小事情,最好更多地关注算法。显然,可读性是一个因素。
答案 12 :(得分:0)
在Java-8中,向Objects
类引入了另外两种方法:
Objects#nonNull和Objects#isNull,可用于替换null
次检查。有趣的是,他们都首先使用对象:
public static boolean isNull(Object obj) {
return obj == null;
}
和
public static boolean nonNull(Object obj) {
return obj != null;
}
相应。我想这意味着这是推荐的方式(至少核心jdk开发人员使用这种方法) Objects source code
答案 13 :(得分: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
});
}
}
答案 14 :(得分:0)
我希望使用null != object
,因为它可以清楚地看到它只是用于null检查。
答案 15 :(得分:-3)
字节代码只是源代码的简单翻译。