哪个更快,在java中尝试catch或if-else(WRT性能)

Rak*_*esh 35 java if-statement try-catch

哪一个更快:

这个

try {
  n.foo();
} 
catch(NullPointerException ex) {
}
Run Code Online (Sandbox Code Playgroud)

要么

if (n != null) n.foo();
Run Code Online (Sandbox Code Playgroud)

Mit*_*eat 53

这不是一个更快的问题,而是一个正确性的问题.

例外的情况恰恰相反.

如果n可以null作为正常业务逻辑的一部分,那么使用an if..else,否则throw为异常.

  • 像往常一样,第一张说"不做过早优化"的海报得到了最多的赞成.这个问题不是关于什么是正确的,最好的或最可读的,而是什么是最快的.OP最有可能在上一期Java专家时事通讯中找到这个问题,这意味着他们可能有兴趣了解原因*为什么*最快的版本更快(以及加深他们对VM内部的理解),而不是考虑在生产代码中实现这一点. (19认同)
  • @Ralph:有时你会超越所要求的,并回答真正的问题. (2认同)

Bor*_*vić 53

if (n != null) n.foo();
Run Code Online (Sandbox Code Playgroud)

是比较快的.

  • +1用于回答被质疑的内容,而不是正确的内容. (39认同)
  • @Rakesh正确性总是比速度更重要.一个运行速度快但不正确的程序是没用的. (9认同)
  • 只有@Jesper的正确性没有回答这个问题.如果是过早优化则不重要.回答问题并在答案中添加警告,而不是仅仅重复"过早优化"福音来升级 (9认同)
  • -1因为你没有解释为什么它更快. (9认同)
  • @Rakesh Juyal:好评!当有人问一个问题并且回复都包括"用例是什么?"时,即使问题很清楚,我真的很烦我.想要知道答案是不够的,而不必为这个问题辩护? (6认同)
  • 你的证据在哪里? (4认同)
  • @Mitch小麦是的,因为它让那些复制粘贴它的人投了一些没被问到的东西.没有理由回答这个问题并给出好的建议是相互排斥的.并不是说过早的优化并不是邪恶的,但对于任何有关性能的问题而言,它并不是一个有效的答案. (2认同)

Ste*_*n C 34

显式测试空指针比使用异常处理快得多.

对于记录,大多数使用异常的oherhead都是在异常对象的实例化中产生的.特别是在呼叫fillInStackTrace()中必须:

  • 检查当前线程堆栈的每个堆栈帧,以及
  • 创建数据结构以捕获堆栈帧详细信息.

在某些情况下,您可以通过重用异常对象或通过覆盖特定于应用程序的异常fillInStackTrace()方法来使其成为无操作来减少此问题.两种情况下的缺点是,将不再提供适当的堆栈跟踪来帮助您调试意外异常.(这些都不适用于OP的例子.)

虽然异常实例化很昂贵,但异常抛出,传播和捕获也不是很便宜.


第二个原因是显式空值测试是一个更好的想法.考虑一下:

try {
    doSomething(a.field);
} catch (NullPointerException ex) {
    System.err.println("a.field is null");
}
Run Code Online (Sandbox Code Playgroud)

如果NPE在调用中发生doSomething(...)而不是在a.field表达式的评估期间会发生什么?当然,我们会捕获一个NPE,但我们会误诊它,然后继续尝试继续......错误地认为这a.field是未设置或其他事情.

区分"预期的"NPE和"意外的"NPE在理论上是可行的,但在实践中非常困难.一种更简单,更健壮的方法是明确地测试null您期望的值(例如使用if语句),并将所有NPE视为错误.

(我确信这就是@Mitch所说的"将异常视为例外"的意思,但我认为通过一个说明性的例子来解释事情是有帮助的......)


Mar*_*arc 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
Run Code Online (Sandbox Code Playgroud)

这对我来说似乎很有说服力.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
Run Code Online (Sandbox Code Playgroud)

使用服务器VM,差异几乎不可察觉.仍然:我宁愿不使用捕获NullPointerException,除非它确实是一个例外.


Enn*_*oji 8

如果n.foo()碰巧在内部抛出一个NPE,那么你需要进行长时间的调试(或者更糟糕的是,你的应用程序在生产中失败了......).只是不要这样做.

无论如何,你打算保存多少纳秒?

  • +1可以节省可能的费用. (3认同)

gus*_*afc 8

I notice I'm not the only one reading the Java Specialist's Newsletter :)

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?

  1. 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.
  2. Some believe that conditional statements are slower because they prevent instruction pipelining, and by avoiding the explicit if they think they got away cheap when n != null. The thing is, however, that the VM will do an implicit null check when dereferencing... that is, unless the JIT can determine that n 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...
  3. ... 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.