Java 6中复合if/or与try/catch的成本
我们目前有以下复合if语句Java 6中复合if/or与try/catch的成本,java,if-statement,try-catch,Java,If Statement,Try Catch,我们目前有以下复合if语句 if ((billingRemoteService == null) || billingRemoteService.getServiceHeader() == null || !"00".equals(billingRemoteService.getServiceHeader().getStatusCode()) || (billingRemoteService.getServiceBody() == null) || (billi
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);
这不能简化为
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);
}
如果是这样,在Java6下,这两种方法之间的“成本”差异是什么?这似乎是一个相当复杂的if语句,用于验证所有中间调用都不是null。对于不同的帐户,此操作会被多次调用。根据经验,异常处理比ifs更昂贵,但我同意Z的观点,即最好的方法是在预期负载下对两个版本进行基准测试/评测。当考虑IO和网络成本时,差异可能变得微不足道,这通常会将CPU成本推算出数量级。
另外,请注意,
!00.在第二个版本中可能应该检查equals
条件。基本上if/else和try-catch是不同的。这些东西的性能/成本与您的代码无关。一旦您正确地设计和实现了业务规则,您就可以担心性能了
try/catch用于程序异常状态的异常处理。而if-else用于程序的条件状态
它们有自己的用途。查看所需内容并相应地编码
而且,你违反了德米特的法律。如果您仔细检查设计和代码,使其更易于阅读,并使其成为更好的可维护性和可扩展性设计,那将更好 正如其他人所说,异常比if语句更昂贵。但是,有一个很好的理由不在您的案例中使用它们 例外情况适用于例外事件 解包消息时,消息中不存在的内容应为错误检查,而不是异常事件 这段代码对其他实例中的数据太感兴趣了。向这些其他实例添加一些行为。现在,所有的行为都在不在类中的代码中,这是错误的对象定向
-- for billingRemoteService --
public boolean hasResponse();
public BillingRemoteResponse getResponse();
-- for BillingRemoteResponse --
public List<Customer> getCustomerList();
-- for Customer --
public Customer(Long ecpdId, ...) {
if (ecpdId == null) throw new IllegalArgumentException(...);
}
——用于billingRemoteService--
公共布尔hassresponse();
public BillingRemoteResponse getResponse();
--用于BillingRemoteResponse--
公共列表getCustomerList();
--为顾客--
公共客户(长ecpdId,…){
如果(ecpdId==null)抛出新的IllegalArgumentException(…);
}
您可以将Groovy混合到基于JVM的应用程序中,这样做会大大简化:
def result = billingRemoteService?.
serviceBody?.
serviceResponse?.
customersList?.
customersList[0];
if ('00' != billingRemoteService?.serviceHeader?.statusCode ||
result?.
billAccountInfo?.
getEcpdId == null)
throw new WebServicesException
...
好的,没有一个答案真正回答了这个问题,尽管根据我目前的情况,theZ的建议是检查这个问题的最快方法。这些代码都不是我设计或编写的,而且它所属的应用程序非常庞大,这意味着要花上数年的时间进行重构,才能处理像这样的各种情况 因此,对于每个人的启迪: 我快速进行了一个测试,模拟了两种方法所需的类。我不在乎类的任何单个方法运行多长时间,因为这与我的问题无关。我还使用JDK1.6和1.7构建/运行。这两个JDK之间几乎没有区别 如果一切正常(任何地方都没有空值),则平均时间为:
Method A (compound IF): 4ms
Method B (exceptions): 2ms
因此,当对象不为null时使用异常的速度是复合异常的两倍
如果我故意在get(0)语句中强制执行空指针异常,事情会变得更加有趣
这里的平均数是:
Method A: 36ms
Method B: 6ms
因此,很明显,在最初记录的案例中,从成本角度来看,例外是可行的。我一定不同意Edwin Buck的论点 他说:
正如其他人所说,异常比if语句更昂贵。但是,有一个很好的理由不在您的案例中使用它们。“例外情况适用于例外事件” 解包消息时,消息中不存在的内容应为错误检查,而不是异常事件 这本质上是说,如果您进行错误检查,那么错误是预期的(因为您正在寻找它),因此不是例外 但这并不是“特殊事件”的意思。异常事件是指异常/异常/不可能发生的事件。例外是指事件发生的可能性,而不是你是否(或应该)期待和/或期待它 因此,回到第一原则,避免异常的基本理由是成本权衡:显式测试事件的成本与抛出、捕获和处理异常的成本。准确地说 如果事件发生的概率为p
- 使用异常的平均成本为: p*创建/抛出/捕获/处理异常的成本+(1-p)*无显式测试的成本
- 不使用异常的平均成本为: p*条件发生时的成本测试和错误处理+(1-p)*条件未发生时的测试成本
因此,在回答最初的问题时,这不仅仅是if/else相对于异常的成本。您还需要考虑您正在测试的事件(错误)的可能性 另一件需要注意的事情是,JIT编译器在优化这两个版本方面有很大的空间
- 在第一个版本中,可能存在大量子表达式的重复计算,以及重复的幕后空值检查。JIT编译器可能能够优化其中的一些,尽管这取决于是否有副作用。如果不能,那么测试的顺序可能会相当昂贵
- 在第二个版本中,有JIT c的作用域
// Version 1 if (someTest()) { doIt(); } else { recover(); } // Version 2 try { doIt(); } catch (SomeException ex) { recover(); }
V1 = cost("someTest") + P * cost("recover") + (1 - P) * cost("doIt-success")
v2 = P * ( cost("doit-fail") + cost("throw/catch") + cost("recover") ) + (1 - P) * cost("doIt-success")
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") )
import java.util.List; import java.util.Optional; public class Optionals { interface BillingRemoteService {Optional<ServiceBody> getServiceBody();} interface ServiceBody {Optional<ServiceResponse> getServiceResponse();} interface ServiceResponse {Optional<List<Customer>> getCustomersList();} interface Customer {Optional<BillAccountInfo> getBillAccountInfo();} interface BillAccountInfo {Optional<EcpId> getEcpdId();} interface EcpId {Optional<BillingRemoteService> getEcpdId();} Object test(BillingRemoteService billingRemoteService) throws Exception { return billingRemoteService.getServiceBody() .flatMap(ServiceBody::getServiceResponse) .flatMap(ServiceResponse::getCustomersList) .map(l->l.get(0)) .flatMap(Optional::ofNullable) .flatMap(Customer::getBillAccountInfo) .flatMap(BillAccountInfo::getEcpdId).orElseThrow(()->new Exception("Failed to get information for Account Number ")); } }