哪一个更快:
这个
try {
n.foo();
}
catch(NullPointerException ex) {
}
或
if (n != null) n.foo();
答案 0 :(得分:53)
if (n != null) n.foo();
更快。
答案 1 :(得分:51)
这不是一个更快的问题,而是一个正确的问题。
例外情况恰恰相反,例外。
如果n
可能null
作为正常业务逻辑的一部分,则使用if..else
,否则throw
例外。
答案 2 :(得分:33)
显式测试空指针比使用异常处理快得多。
对于记录,使用异常的大多数的oherhead在异常对象的实例化中产生。特别是在调用fillInStackTrace()
时必须:
在某些情况下,您可以通过重用异常对象或通过覆盖特定于应用程序的异常fillInStackTrace()
方法来使其成为无操作来减少此问题。两种情况下的缺点是,将不再提供适当的堆栈跟踪来帮助您调试意外异常。 (这些都不适用于OP的例子。)
虽然异常实例化很昂贵,但异常抛出,传播和捕获也不是很便宜。
第二个原因是显式空值测试是一个更好的主意。考虑一下:
try {
doSomething(a.field);
} catch (NullPointerException ex) {
System.err.println("a.field is null");
}
如果NPE在doSomething(...)
的调用中发生而不是在评估a.field
表达式时发生了什么?当然,我们会捕获一个NPE,但我们会误诊它,然后继续尝试继续...错误地假设a.field
未设置或其他东西。
将“预期的”NPE与“意外的”NPE区分开来在理论上是可行的,但在实践中非常困难。一种更简单,更健壮的方法是明确测试您期望的null
值(例如使用if
语句),并将所有NPE视为错误。
(我确信这就是@Mitch的意思是“将异常视为例外”,但我认为通过一个说明性的例子来解释事情会有所帮助......)
答案 3 :(得分:24)
答案并不像看起来那么简单,因为这取决于对象实际上为空的次数百分比。当这种情况非常罕见时(例如在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
这对我来说似乎很有说服力。 NPE的速度非常慢。 (如果需要,我可以发布基准代码)
编辑: 我刚刚发现了一个有趣的发现:使用服务器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
使用服务器VM,差异很难说明。仍然:我宁愿不使用捕获NullPointerException,除非它确实是一个例外。
答案 4 :(得分:8)
如果n.foo()
碰巧在内部抛出一个NPE,那么你需要进行长时间的调试(或者更糟糕的是,你的应用程序在生产中失败了......)。只是不要这样做。
你计划保存多少纳秒?
答案 5 :(得分:7)
我注意到我并不是唯一一位阅读Java专家时事通讯的人:)
除了语义差异这一事实(NPE不一定是由解除引用n
引起的,它可能是由foo()
中的某些错误引发的,以及可读性问题(try / catch对于读者而言比if
更令人困惑),在n != null
(与n == null
的情况下,它们应该同样快。 if / else版本略有优势),但n == null
if / else更快。为什么呢?
if
时,VM必须创建一个新的异常对象并填写其堆栈跟踪。堆栈跟踪信息的获取非常昂贵,因此这里的try / catch版本要贵得多。n != null
,他们认为在n
时它们便宜了。但是,当解除引用时,VM将执行隐式空检查 ...即,除非JIT可以确定{{1}}必须为非空,否则它可以在if / else版本中。这意味着if / else和try / catch版本应该执行大致相同的操作。但是... 答案 6 :(得分:3)
除了好的答案(使用例外的例外情况),我发现你基本上都试图避免在任何地方进行空检查。 Java 7将有一个“null safe”运算符,当调用n?.foo()
而不是抛出NPE时,它将返回null。这是借用Groovy语言的。除非真正需要(即:处理库),否则还有一种趋势是避免在一个代码中完全使用null。有关此问题的更多讨论,请参阅此其他答案。
Avoiding != null statements
答案 7 :(得分:2)
处理异常通常很昂贵。 VM Spec可能会让您了解多少,但在上述情况if (n != null) n.foo();
更快。
虽然我同意Mitch Wheat关于真正问题的正确性。
@Mitch Wheat - 在他的辩护中,这是一个非常人为的例子。 :)答案 8 :(得分:1)
if 构造更快。条件可以很容易地转换为机器代码(处理器指令)。
替代方法( try-catch )需要创建NullPointerException对象。
答案 9 :(得分:0)
绝对第二种形式要快得多。在try-catch
方案中,它会抛出一个exception
来执行某种形式的new Exception()
。然后调用catch
块,这是一个方法调用,必须执行其中的任何代码。你明白了。
答案 10 :(得分:0)
首先是if ..然后..其他更好,由于其他海报指出的众多原因。
然而,它的速度并不快!它非常依赖于null对象与非null对象的比例。它可能需要数十万倍的资源来处理异常而不是测试null,但是,如果null对象每百万个对象只出现一次,那么异常选项会稍快一些。但速度不是那么快,它使你的程序可读性更低,更难以调试。
答案 11 :(得分:0)
Heinz博士最近讨论过这个问题:
http://javaspecialists.eu/webinars/recordings/if-else-npe-teaser.mov
答案 12 :(得分:0)
if-else更快,因为try-catch块引发了异常堆栈跟踪。 您可以将其作为If-Else块执行一条指令来进行评估,但Try-Catch将运行数千条指令以在发生异常时引发异常。