Which one is faster:
哪一个更快:
Either this
try {
n.foo();
}
catch(NullPointerException ex) {
}
or
if (n != null) n.foo();
13 个解决方案
#1
52
It's not a question of which is faster, rather one of correctness.
这不是一个更快的问题,而是一个正确性的问题。
An exception is for circumstances which are exactly that, exceptional.
例外的情况恰恰相反。
If it is possible for n
to be null
as part of normal business logic, then use an if..else
, else throw
an exception.
如果n可能作为普通业务逻辑的一部分为null,则使用if..else,否则抛出异常。
#2
50
if (n != null) n.foo();
is faster.
#3
33
Explicitly testing for a null pointer is much faster than using exception handling.
显式测试空指针比使用异常处理快得多。
For the record, most of the oherheads in using exceptions are incurred in the instantiation of the exception object. In particular in the call to fillInStackTrace()
which has to:
对于记录,大多数使用异常的oherhead都是在异常对象的实例化中产生的。特别是在调用fillInStackTrace()时必须:
- examine every stack frame for the current thread's stack, and
- create a data structure to capture the stack frame details.
检查当前线程堆栈的每个堆栈帧,以及
创建数据结构以捕获堆栈帧详细信息。
In some cases, you can reduce this by reusing the exception object, or by overriding an application specific exception's fillInStackTrace()
method to make it a no-op. The downside in both cases is that proper stacktraces will no longer be available to help you debug unexpected exceptions. (And neither of these are applicable to the OP's example.)
在某些情况下,您可以通过重用异常对象或通过覆盖特定于应用程序的异常的fillInStackTrace()方法来使其成为无操作来减少这种情况。两种情况下的缺点是,将无法再使用适当的堆栈跟踪来帮助您调试意外异常。 (这些都不适用于OP的例子。)
While exception instantiation is expensive, exception throwing, propagation and catching are not exactly cheap either.
虽然异常实例化很昂贵,但异常抛出,传播和捕获也不是很便宜。
There is a second reason why explicit null testing is a better idea. Consider this:
第二个原因是显式空值测试是一个更好的想法。考虑一下:
try {
doSomething(a.field);
} catch (NullPointerException ex) {
System.err.println("a.field is null");
}
What happens if an NPE happens within the call to doSomething(...)
instead of during the evaluation of the a.field
expression? Sure, we'll catch an NPE, but we will misdiagnose it, and then attempt to continue ... incorrectly assuming that a.field
is unset or something.
如果在调用doSomething(...)而不是在评估a.field表达式时发生NPE会发生什么?当然,我们会捕获一个NPE,但我们会误诊它,然后继续尝试继续...错误地假设a.field未设置或其他什么。
Distinguishing an "expected" NPE from an "unexpected" NPE is theoretically possible, but in practice very difficult. A much simpler and more robust approach is to explicitly test for the null
values that you are expecting (e.g. with an if
statement), and treat all NPEs as bugs.
区分“预期的”NPE和“意外的”NPE在理论上是可行的,但在实践中非常困难。一种更简单,更健壮的方法是显式测试您期望的空值(例如使用if语句),并将所有NPE视为错误。
(I'm sure that this is what @Mitch means by "treating exceptions as exceptional", but I think it helps to spell things out with an illustrative example ...)
(我确信这就是@Mitch所说的“将异常视为例外”的意思,但我认为通过一个说明性的例子来解释事情是有帮助的......)
#4
23
The answer to this is not as simple as it looks, because this will depend on the percentage of times that the object is really null. When this is very uncommon (say in 0.1% of the time), it might even be faster. To test this I've done some benchmarking with the following results (with Java 1.6 client):
答案并不像看起来那么简单,因为这取决于对象实际为空的次数百分比。当这种情况非常罕见时(例如在0.1%的情况下),它甚至可能更快。为了测试这个,我用以下结果做了一些基准测试(使用Java 1.6客户端):
Benchmaring with factor 1.0E-4
Average time of NullIfTest: 0.44 seconds
Average time of NullExceptionTest: 0.45 seconds
Benchmaring with factor 0.0010
Average time of NullIfTest: 0.44 seconds
Average time of NullExceptionTest: 0.46 seconds
Benchmaring with factor 0.01
Average time of NullIfTest: 0.42 seconds
Average time of NullExceptionTest: 0.52 seconds
Benchmaring with factor 0.1
Average time of NullIfTest: 0.41 seconds
Average time of NullExceptionTest: 1.30 seconds
Benchmaring with factor 0.9
Average time of NullIfTest: 0.07 seconds
Average time of NullExceptionTest: 7.48 seconds
This seems pretty conclusive to me. NPE's are just very slow. (I can post the benchmarking code if wanted)
这对我来说似乎很有说服力。 NPE的速度非常慢。 (如果需要,我可以发布基准测试代码)
edit: I've just made an interesting discovery: when benchmarking using the server JVM, the results change drastically:
编辑:我刚刚发现了一个有趣的发现:使用服务器JVM进行基准测试时,结果会发生巨大变化:
Benchmaring with factor 1.0E-4
Average time of NullIfTest: 0.33 seconds
Average time of NullExceptionTest: 0.33 seconds
Benchmaring with factor 0.0010
Average time of NullIfTest: 0.32 seconds
Average time of NullExceptionTest: 0.33 seconds
Benchmaring with factor 0.01
Average time of NullIfTest: 0.31 seconds
Average time of NullExceptionTest: 0.32 seconds
Benchmaring with factor 0.1
Average time of NullIfTest: 0.28 seconds
Average time of NullExceptionTest: 0.30 seconds
Benchmaring with factor 0.9
Average time of NullIfTest: 0.05 seconds
Average time of NullExceptionTest: 0.04 seconds
Using the server VM, the difference is hardly noticable. Still: I'd rather not use catching NullPointerException unless it really is an exception.
使用服务器VM,差异几乎不可察觉。仍然:我宁愿不使用捕获NullPointerException,除非它确实是一个例外。
#5
8
If n.foo()
happens to throw internally a NPE, you are off for a long debugging session (or worse, your app fails in production..). Just don't do it.
如果n.foo()碰巧在内部抛出一个NPE,那么你需要进行长时间的调试(或者更糟糕的是,你的应用程序在生产中失败了......)。只是不要这样做。
How many nano-seconds do you plan to save, anyways?
无论如何,你计划保存多少纳秒?
#6
6
I notice I'm not the only one reading the Java Specialist's Newsletter :)
我注意到我不是唯一一个阅读Java专家新闻通讯的人:)
Apart from the fact that there's a semantic difference (the NPE isn't necessarily caused by dereferencing n
, it might have been thrown by some error in foo()
), and a readability issue (the try/catch is more confusing to a reader than the if
), they should be about equally fast in the case when n != null
(with the if/else version having a slight advantage), but when n == null
if/else is a lot faster. Why?
除了存在语义差异这一事实(NPE不一定是由解除引用n引起的,它可能是由于foo()中的某些错误引起的),以及可读性问题(try / catch对读者来说更加困惑)比if),在n!= null(if / else版本略有优势)的情况下它们应该同样快,但是当n == null时如果/ else更快。为什么?
- When
n == null
, the VM must create a new exception object and fill in its stack trace. The stack trace info is really expensive to acquire, so here the try/catch version is far more expensive. - Some believe that conditional statements are slower because they prevent instruction pipelining, and by avoiding the explicit
if
they think they got away cheap whenn != null
. The thing is, however, that the VM will do an implicit null check when dereferencing... that is, unless the JIT can determine thatn
must be non-null, which it can in the if/else version. This means that the if/else and try/catch versions should be perform approximately the same. But... - ... try/catch clauses can interfere with how the JIT can inline method calls, which means that it might not be able to optimize the try/catch version as well as the if/else.
当n == null时,VM必须创建一个新的异常对象并填写其堆栈跟踪。堆栈跟踪信息的获取非常昂贵,因此这里的try / catch版本要贵得多。
有些人认为条件语句较慢,因为它们会阻止指令流水线操作,并且如果他们认为在n!= null时他们认为它们便宜,则可以避免显式。但是,当解除引用时,VM将执行隐式空检查...即,除非JIT可以确定n必须是非空的,否则它可以在if / else版本中。这意味着if / else和try / catch版本应该执行大致相同的操作。但...
... try / catch子句可能会干扰JIT如何内联方法调用,这意味着它可能无法优化try / catch版本以及if / else。
#7
3
Beside the good answers (use exceptions for exceptional cases) I see that you're basically trying to avoid the null checks everywhere. Java 7 will have a "null safe" operator that will return null when n?.foo()
is called instead of throwing a NPE. That's borrowed from the Groovy language. There's also a trend to avoid using null altogether in one's code except when really needed (ie: dealing with libraries). See this other answer for more discussion on this. Avoiding != null statements
除了好的答案(使用例外的例外情况),我发现你基本上都试图避免在任何地方进行空检查。 Java 7将有一个“null safe”运算符,当调用n?.foo()而不是抛出NPE时,它将返回null。这是从Groovy语言借来的。除非真正需要(即:处理库),否则还有一种趋势是避免在一个代码中完全使用null。有关此问题的更多讨论,请参阅此其他答案。避免!= null语句
#8
2
It is usually expensive to handle exceptions. The VM Spec might give you some insight into how much, but in the above case if (n != null) n.foo();
is faster.
处理异常通常很昂贵。 VM Spec可能会让你深入了解多少,但在上面的例子中if(n!= null)n.foo();是比较快的。
Although I agree with Mitch Wheat regarding the real question is correctness.
虽然我同意Mitch Wheat关于真正问题的正确性。
@Mitch Wheat - In his defense this is a pretty contrived example. :)
@Mitch Wheat - 在他的辩护中,这是一个非常人为的例子。 :)
#9
1
The if construct is faster. The condition can be easily translated to machine code (processor instructions).
if构造更快。条件可以很容易地转换为机器代码(处理器指令)。
The alternative (try-catch) requires creating a NullPointerException object.
替代方法(try-catch)需要创建一个NullPointerException对象。
#10
0
Definitely second form is much faster. In the try-catch
scenario, it throws an exception
which does a new Exception()
of some form. Then the catch
block is invoked which is a method call and has to execute whatever code is in it. You get idea.
绝对第二种形式要快得多。在try-catch场景中,它抛出一个异常,它执行某种形式的新Exception()。然后调用catch块,这是一个方法调用,必须执行其中的任何代码。你明白了。
#11
0
Firstly the if.. then .. else is better, for numerous reasons pointed out by the other posters.
首先是if ..然后..其他更好,由于其他海报指出的众多原因。
However it is not necceseraly faster! It depends entirly on the ration of null objects to not null objects. It probably takes a hundreds of thousands times the resources to process an exception rather than test for null, however, if a null object occurs only once for every million objects then the exception option will be slightly faster. But not that much faster that its worth making your program less readable and harder to debug.
然而,它并不是更快!它非常依赖于null对象与非null对象的比例。它可能需要数十万倍的资源来处理异常而不是测试null,但是,如果每个百万个对象只出现一次空对象,那么异常选项会稍快一些。但是,它的价值不是那么快,它使您的程序可读性更低,更难以调试。
#12
0
if-else is faster because a try-catch block raises an exception stack trace. You can take it as the If-Else block is executing one instruction to do the evaluation, but the Try-Catch will run thousands of instructions to raise the exception when it happens.
if-else更快,因为try-catch块引发异常堆栈跟踪。您可以将其视为If-Else块正在执行一条指令来进行评估,但Try-Catch将运行数千条指令以在发生异常时引发异常。
#13
0
This issue has discussed recently by Dr. Heinz:
Heinz博士最近讨论过这个问题:
http://javaspecialists.eu/webinars/recordings/if-else-npe-teaser.mov
#1
52
It's not a question of which is faster, rather one of correctness.
这不是一个更快的问题,而是一个正确性的问题。
An exception is for circumstances which are exactly that, exceptional.
例外的情况恰恰相反。
If it is possible for n
to be null
as part of normal business logic, then use an if..else
, else throw
an exception.
如果n可能作为普通业务逻辑的一部分为null,则使用if..else,否则抛出异常。
#2
50
if (n != null) n.foo();
is faster.
#3
33
Explicitly testing for a null pointer is much faster than using exception handling.
显式测试空指针比使用异常处理快得多。
For the record, most of the oherheads in using exceptions are incurred in the instantiation of the exception object. In particular in the call to fillInStackTrace()
which has to:
对于记录,大多数使用异常的oherhead都是在异常对象的实例化中产生的。特别是在调用fillInStackTrace()时必须:
- examine every stack frame for the current thread's stack, and
- create a data structure to capture the stack frame details.
检查当前线程堆栈的每个堆栈帧,以及
创建数据结构以捕获堆栈帧详细信息。
In some cases, you can reduce this by reusing the exception object, or by overriding an application specific exception's fillInStackTrace()
method to make it a no-op. The downside in both cases is that proper stacktraces will no longer be available to help you debug unexpected exceptions. (And neither of these are applicable to the OP's example.)
在某些情况下,您可以通过重用异常对象或通过覆盖特定于应用程序的异常的fillInStackTrace()方法来使其成为无操作来减少这种情况。两种情况下的缺点是,将无法再使用适当的堆栈跟踪来帮助您调试意外异常。 (这些都不适用于OP的例子。)
While exception instantiation is expensive, exception throwing, propagation and catching are not exactly cheap either.
虽然异常实例化很昂贵,但异常抛出,传播和捕获也不是很便宜。
There is a second reason why explicit null testing is a better idea. Consider this:
第二个原因是显式空值测试是一个更好的想法。考虑一下:
try {
doSomething(a.field);
} catch (NullPointerException ex) {
System.err.println("a.field is null");
}
What happens if an NPE happens within the call to doSomething(...)
instead of during the evaluation of the a.field
expression? Sure, we'll catch an NPE, but we will misdiagnose it, and then attempt to continue ... incorrectly assuming that a.field
is unset or something.
如果在调用doSomething(...)而不是在评估a.field表达式时发生NPE会发生什么?当然,我们会捕获一个NPE,但我们会误诊它,然后继续尝试继续...错误地假设a.field未设置或其他什么。
Distinguishing an "expected" NPE from an "unexpected" NPE is theoretically possible, but in practice very difficult. A much simpler and more robust approach is to explicitly test for the null
values that you are expecting (e.g. with an if
statement), and treat all NPEs as bugs.
区分“预期的”NPE和“意外的”NPE在理论上是可行的,但在实践中非常困难。一种更简单,更健壮的方法是显式测试您期望的空值(例如使用if语句),并将所有NPE视为错误。
(I'm sure that this is what @Mitch means by "treating exceptions as exceptional", but I think it helps to spell things out with an illustrative example ...)
(我确信这就是@Mitch所说的“将异常视为例外”的意思,但我认为通过一个说明性的例子来解释事情是有帮助的......)
#4
23
The answer to this is not as simple as it looks, because this will depend on the percentage of times that the object is really null. When this is very uncommon (say in 0.1% of the time), it might even be faster. To test this I've done some benchmarking with the following results (with Java 1.6 client):
答案并不像看起来那么简单,因为这取决于对象实际为空的次数百分比。当这种情况非常罕见时(例如在0.1%的情况下),它甚至可能更快。为了测试这个,我用以下结果做了一些基准测试(使用Java 1.6客户端):
Benchmaring with factor 1.0E-4
Average time of NullIfTest: 0.44 seconds
Average time of NullExceptionTest: 0.45 seconds
Benchmaring with factor 0.0010
Average time of NullIfTest: 0.44 seconds
Average time of NullExceptionTest: 0.46 seconds
Benchmaring with factor 0.01
Average time of NullIfTest: 0.42 seconds
Average time of NullExceptionTest: 0.52 seconds
Benchmaring with factor 0.1
Average time of NullIfTest: 0.41 seconds
Average time of NullExceptionTest: 1.30 seconds
Benchmaring with factor 0.9
Average time of NullIfTest: 0.07 seconds
Average time of NullExceptionTest: 7.48 seconds
This seems pretty conclusive to me. NPE's are just very slow. (I can post the benchmarking code if wanted)
这对我来说似乎很有说服力。 NPE的速度非常慢。 (如果需要,我可以发布基准测试代码)
edit: I've just made an interesting discovery: when benchmarking using the server JVM, the results change drastically:
编辑:我刚刚发现了一个有趣的发现:使用服务器JVM进行基准测试时,结果会发生巨大变化:
Benchmaring with factor 1.0E-4
Average time of NullIfTest: 0.33 seconds
Average time of NullExceptionTest: 0.33 seconds
Benchmaring with factor 0.0010
Average time of NullIfTest: 0.32 seconds
Average time of NullExceptionTest: 0.33 seconds
Benchmaring with factor 0.01
Average time of NullIfTest: 0.31 seconds
Average time of NullExceptionTest: 0.32 seconds
Benchmaring with factor 0.1
Average time of NullIfTest: 0.28 seconds
Average time of NullExceptionTest: 0.30 seconds
Benchmaring with factor 0.9
Average time of NullIfTest: 0.05 seconds
Average time of NullExceptionTest: 0.04 seconds
Using the server VM, the difference is hardly noticable. Still: I'd rather not use catching NullPointerException unless it really is an exception.
使用服务器VM,差异几乎不可察觉。仍然:我宁愿不使用捕获NullPointerException,除非它确实是一个例外。
#5
8
If n.foo()
happens to throw internally a NPE, you are off for a long debugging session (or worse, your app fails in production..). Just don't do it.
如果n.foo()碰巧在内部抛出一个NPE,那么你需要进行长时间的调试(或者更糟糕的是,你的应用程序在生产中失败了......)。只是不要这样做。
How many nano-seconds do you plan to save, anyways?
无论如何,你计划保存多少纳秒?
#6
6
I notice I'm not the only one reading the Java Specialist's Newsletter :)
我注意到我不是唯一一个阅读Java专家新闻通讯的人:)
Apart from the fact that there's a semantic difference (the NPE isn't necessarily caused by dereferencing n
, it might have been thrown by some error in foo()
), and a readability issue (the try/catch is more confusing to a reader than the if
), they should be about equally fast in the case when n != null
(with the if/else version having a slight advantage), but when n == null
if/else is a lot faster. Why?
除了存在语义差异这一事实(NPE不一定是由解除引用n引起的,它可能是由于foo()中的某些错误引起的),以及可读性问题(try / catch对读者来说更加困惑)比if),在n!= null(if / else版本略有优势)的情况下它们应该同样快,但是当n == null时如果/ else更快。为什么?
- When
n == null
, the VM must create a new exception object and fill in its stack trace. The stack trace info is really expensive to acquire, so here the try/catch version is far more expensive. - Some believe that conditional statements are slower because they prevent instruction pipelining, and by avoiding the explicit
if
they think they got away cheap whenn != null
. The thing is, however, that the VM will do an implicit null check when dereferencing... that is, unless the JIT can determine thatn
must be non-null, which it can in the if/else version. This means that the if/else and try/catch versions should be perform approximately the same. But... - ... try/catch clauses can interfere with how the JIT can inline method calls, which means that it might not be able to optimize the try/catch version as well as the if/else.
当n == null时,VM必须创建一个新的异常对象并填写其堆栈跟踪。堆栈跟踪信息的获取非常昂贵,因此这里的try / catch版本要贵得多。
有些人认为条件语句较慢,因为它们会阻止指令流水线操作,并且如果他们认为在n!= null时他们认为它们便宜,则可以避免显式。但是,当解除引用时,VM将执行隐式空检查...即,除非JIT可以确定n必须是非空的,否则它可以在if / else版本中。这意味着if / else和try / catch版本应该执行大致相同的操作。但...
... try / catch子句可能会干扰JIT如何内联方法调用,这意味着它可能无法优化try / catch版本以及if / else。
#7
3
Beside the good answers (use exceptions for exceptional cases) I see that you're basically trying to avoid the null checks everywhere. Java 7 will have a "null safe" operator that will return null when n?.foo()
is called instead of throwing a NPE. That's borrowed from the Groovy language. There's also a trend to avoid using null altogether in one's code except when really needed (ie: dealing with libraries). See this other answer for more discussion on this. Avoiding != null statements
除了好的答案(使用例外的例外情况),我发现你基本上都试图避免在任何地方进行空检查。 Java 7将有一个“null safe”运算符,当调用n?.foo()而不是抛出NPE时,它将返回null。这是从Groovy语言借来的。除非真正需要(即:处理库),否则还有一种趋势是避免在一个代码中完全使用null。有关此问题的更多讨论,请参阅此其他答案。避免!= null语句
#8
2
It is usually expensive to handle exceptions. The VM Spec might give you some insight into how much, but in the above case if (n != null) n.foo();
is faster.
处理异常通常很昂贵。 VM Spec可能会让你深入了解多少,但在上面的例子中if(n!= null)n.foo();是比较快的。
Although I agree with Mitch Wheat regarding the real question is correctness.
虽然我同意Mitch Wheat关于真正问题的正确性。
@Mitch Wheat - In his defense this is a pretty contrived example. :)
@Mitch Wheat - 在他的辩护中,这是一个非常人为的例子。 :)
#9
1
The if construct is faster. The condition can be easily translated to machine code (processor instructions).
if构造更快。条件可以很容易地转换为机器代码(处理器指令)。
The alternative (try-catch) requires creating a NullPointerException object.
替代方法(try-catch)需要创建一个NullPointerException对象。
#10
0
Definitely second form is much faster. In the try-catch
scenario, it throws an exception
which does a new Exception()
of some form. Then the catch
block is invoked which is a method call and has to execute whatever code is in it. You get idea.
绝对第二种形式要快得多。在try-catch场景中,它抛出一个异常,它执行某种形式的新Exception()。然后调用catch块,这是一个方法调用,必须执行其中的任何代码。你明白了。
#11
0
Firstly the if.. then .. else is better, for numerous reasons pointed out by the other posters.
首先是if ..然后..其他更好,由于其他海报指出的众多原因。
However it is not necceseraly faster! It depends entirly on the ration of null objects to not null objects. It probably takes a hundreds of thousands times the resources to process an exception rather than test for null, however, if a null object occurs only once for every million objects then the exception option will be slightly faster. But not that much faster that its worth making your program less readable and harder to debug.
然而,它并不是更快!它非常依赖于null对象与非null对象的比例。它可能需要数十万倍的资源来处理异常而不是测试null,但是,如果每个百万个对象只出现一次空对象,那么异常选项会稍快一些。但是,它的价值不是那么快,它使您的程序可读性更低,更难以调试。
#12
0
if-else is faster because a try-catch block raises an exception stack trace. You can take it as the If-Else block is executing one instruction to do the evaluation, but the Try-Catch will run thousands of instructions to raise the exception when it happens.
if-else更快,因为try-catch块引发异常堆栈跟踪。您可以将其视为If-Else块正在执行一条指令来进行评估,但Try-Catch将运行数千条指令以在发生异常时引发异常。
#13
0
This issue has discussed recently by Dr. Heinz:
Heinz博士最近讨论过这个问题:
http://javaspecialists.eu/webinars/recordings/if-else-npe-teaser.mov