本文介绍了Java 6中复合if /或与try / catch的成本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们目前有以下复合if语句...

We currently have the following compound if statement...

if ((billingRemoteService == null)
    || billingRemoteService.getServiceHeader() == null
    || !"00".equals(billingRemoteService.getServiceHeader().getStatusCode())
    || (billingRemoteService.getServiceBody() == null) 
    || (billingRemoteService.getServiceBody().getServiceResponse() == null) 
    || (billingRemoteService.getServiceBody().getServiceResponse().getCustomersList() == null) 
    || (billingRemoteService.getServiceBody().getServiceResponse().getCustomersList().getCustomersList() == null) 
    || (billingRemoteService.getServiceBody().getServiceResponse().getCustomersList().getCustomersList().get(0) == null) 
    || (billingRemoteService.getServiceBody().getServiceResponse().getCustomersList().getCustomersList().get(0).getBillAccountInfo() == null)
    || (billingRemoteService.getServiceBody().getServiceResponse().getCustomersList().getCustomersList().get(0).getBillAccountInfo().getEcpdId() == null)) {
        throw new WebservicesException("Failed to get information for Account Number " + accountNo);
}

return billingRemoteService.getServiceBody().getServiceResponse().getCustomersList().getCustomersList().get(0);

这不能简化为......

Couldn't this be simplified as ...

try {
    //Check to be sure there is an EpcdId.
    (billingRemoteService.getServiceBody().getServiceResponse().getCustomersList().getCustomersList().get(0).getBillAccountInfo().getEcpdId();
    return billingRemoteService.getServiceBody().getServiceResponse().getCustomersList().getCustomersList().get(0);
} catch (NullPointerException npe) {
    throw new WebservicesException("Failed to get information for Account Number " + accountNo);
}

如果是这样,Java 6下两种方法之间的成本差异是什么?似乎就像一个非常复杂的if语句,只是为了验证所有插入的调用都不是null。对于不同的帐户,这个操作被多次调用。

If so, what is the "cost" difference between the two approaches under Java 6? It seems like a pretty complex if statement just to verify that all of the intervening calls aren't null. This operation is called many times for different accounts.

推荐答案

我必须不同意Edwin Buck的观点。

I must disagree with Edwin Buck's argument.

他说:

解压缩消息时,消息中不存在错误检查,而非异常事件。

When unpacking a message, something not being in the message is expected error checking, not an exceptional event.

这实质上是说如果你做错误检查,那么预计会出错(因为你正在寻找它),因此也不例外。

This is essentially saying that if you do error checking then an error is expected (because you were looking for it) and therefore not exceptional.

但这不是特殊事件的意思。特殊事件意味着异常/异常/不太可能发生的事件。特殊情况是关于事件发生的可能性,而不是关于你是否(或应该)期待和/或寻找它。

But that is not what "exceptional event" means. An exceptional event means an unusual / out of the ordinary / unlikely event. Exceptional is about the likelihood of the event happening, not about whether you are (or should be) expecting and/or looking for it.

所以回到第一原则,避免异常的根本原因是成本权衡:明确测试事件的成本与抛出,捕获和处理异常的成本。确切地说。

So going back to first principles, the underlying reasoning for avoiding exceptions is a cost trade-off: the cost of explicitly testing for the event versus the cost of throwing, catching and handling the exception. To be precise.


  • 使用例外的平均成本是:

  • the average cost of using exceptions is:

P *创建/抛出/捕获/处理异常的成本+(1 - P)*没有明确测试的成本

P * cost of an exception being created/thrown/caught/handled + (1 - P) * cost of no explicit tests

不使用例外的平均成本是:

the average cost of not using exceptions is:

P *成本测试时发生的情况并进行错误处理+(1 - P)*测试成本,当条件没有发生时。

P * cost testing for the condition when it occurs and doing the error handling + (1 - P) * cost of testing when the condition doesn't occur.

当然,这是例外==不太可能的地方。因为,如果P接近0,则使用的开销例外变得越来越不重要。如果P足够小(取决于问题),异常将更有效。

And of course, this is where "exceptional" == "unlikely" comes in. Because, if as P gets closer to 0, the overheads of using exceptions become less and less significant. And if P is sufficiently small (depending on the problem), exceptions will be MORE efficient.

所以回答原始问题,不仅仅是if / else与异常的成本。您还需要考虑您正在测试的事件(错误)的似然

So in answer to the original question, it is not simply the cost of if / else versus exceptions. You also need to take account of the likelihood of the event (error) that you are testing for.

另外需要注意的是JIT编译器有很多范围可以优化这两个版本。

The other thing to note is that there is a lot of scope for the JIT compiler to optimize both versions.


  • 在第一个版本中,可能会重复计算子表达式,并重复幕后空值检查。 JIT编译器可能能够优化其中的一部分,但这取决于是否存在副作用。如果不能,那么测试序列可能相当昂贵。

  • In the first version, there is potentially a lot repeated calculation of subexpressions, and repeated behind-the-scenes null checking. The JIT compiler may be able to optimize some of this, though it depends whether there might side-effects. If it can't, then the sequence of tests could be rather expensive.

在第二个版本中,JIT编译器可以注意到异常被抛出并在不使用异常对象的情况下捕获相同的方法。由于异常对象没有逃逸,它(理论上)可以被优化掉。如果发生这种情况,使用异常的开销几乎会消失。

In the second version, there is scope for the JIT compiler to notice that an exception is being thrown and caught in the same method without making use of the exception object. Since the exception object doesn't "escape" it could (in theory) be optimized away. And if that happen, the overheads of using exceptions will almost vanish.

(这是一个有用的例子,说明我的非正式方程式意味着什么:

(Here is a worked example to make it clear what my informal equations mean:

  // Version 1
  if (someTest()) {
      doIt();
  } else {
      recover();
  }

  // Version 2
  try {
      doIt();
  } catch (SomeException ex) {
      recover();
  }

如前所述,让P为导致异常的概率

As before, let P be the probability that the exception-causing even occurs.

版本#1 - 如果我们假设测试成功失败,那么 someTest()的成本是相同的,并使用doIt-success来表示成本当没有抛出异常时,那么一次执行版本#1的平均成本是:

Version #1 - if we assume that the cost of someTest() is the same whether the test succeeds of fail, and use "doIt-success" to denote the cost of doIt when no exception is thrown, then the average cost of one execution of version #1 is:

  V1 = cost("someTest") + P * cost("recover") + (1 - P) * cost("doIt-success")

版本#2 - 如果我们假设 doIt()的成本是相同的,无论是否抛出异常,那么一次执行版本#2的平均成本是:

Version #2 - if we assume that the cost of doIt() is the same whether or not an exception is thrown, then the average cost of one execution of version #2 is:

  v2 = P * ( cost("doit-fail") + cost("throw/catch") + cost("recover") ) +
       (1 - P) * cost("doIt-success")

我们从另一个中扣除一个以得出平均成本的差异。

We subtract one from the other to give the difference in average costs.

  V1 - V2 = cost("someTest") + P * cost("recover") + 
            (1 - P) * cost("doIt-success") -
            P * cost("doit-fail") - P * cost("throw/catch") -
            P * cost("recover") - (1 - P) * cost("doIt-success")

          = cost("someTest") - P * ( cost("doit-fail") + cost("throw/catch") )

请注意 recover()的费用以及 doIt()成功取消。我们留下了一个积极的成分(为避免异常而进行测试的成本)和一个与失败概率成正比的负面成分。该等式告诉我们无论抛出/捕获开销是多么昂贵,如果概率 P 接近于零,差异将是否定的

Notice that the costs of recover() and the costs where doIt() succeed cancel out. We are left with a positive component (the cost doing the test to avoid the exception) and a negative component that is proportional to probability of the failure. The equation tells us that no matter how expensive the throw / catch overheads are, if the probability P is close enough to zero, the difference will be negative.

回复此评论:

这与Edwin Bucks的论点非常相似。

This is really the same argument as Edwin Bucks's.

问题是流量控制是什么意思?

The problem is what does "flow control" mean?


  • 一方面,抛出和捕获异常是流量控制的一种形式。所以这意味着你永远不应该抛出并捕获未经检查的异常。这显然没有任何意义。

  • On the one hand, throwing and catching exceptions is a form of flow control. So that would mean that you should never throw and catch unchecked exceptions. That clearly makes no sense.

然后我们回过头来争论不同类型的流量控制,这实际上是关于什么是特殊的争论与非特殊相比。

So then we fall back to arguing about different kinds of flow control, which is really the same arguing about what is "exceptional" versus what it "non-exceptional".

我认识到在捕捉NPE时需要小心,类似于确保你没有捕获来自意外来源的一个(即一个不同的bug)。但在OP的例子中,风险很小。你可以而且应该检查看起来像简单吸气剂的那些东西真的是简单的吸气剂。

I recognize that you need to be careful when catching NPE's and similar to make sure that you don't catch one that comes from an unexpected source (i.e. a different bug). But in the OP's example, there is minimal risk of that. And you can and should check that those things that look like simple getters really are simple getters.

你还必须认识到捕捉NPE(在这种情况下)导致更简单的代码,可能比 if 语句中的长序列条件更可靠。请记住,这种模式可以在很多地方复制。

And you also have to recognize that catching NPE (in this case) results in simpler code, that is likely to be more reliable than a long sequence of conditions in an if statement. Bear in mind that this "pattern" could be replicated in lots of places.

底线是异常和测试之间的选择可能很复杂。一个简单的口头禅,告诉你总是使用测试,在某些情况下会给你错误的解决方案。并且错误可能不太可靠和/或可读性较差和/或代码较慢。

The bottom line is that the choice between exceptions and tests CAN BE a complicated. A simple mantra which tells you to always use tests is going to give you the wrong solution in some situations. And the "wrongness" could be less reliable and/or less readable and/or slower code.

这篇关于Java 6中复合if /或与try / catch的成本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-10 23:45