Which is better/more efficient: check for bad valu

2019-01-23 05:27发布

Which is more efficient in Java: to check for bad values to prevent exceptions or let the exceptions happen and catch them?

Here are two blocks of sample code to illustrate this difference:

void doSomething(type value1) {
    ResultType result = genericError;

     if (value1 == badvalue || value1 == badvalue2 || ...) {
          result = specificError;
     } else {
          DoSomeActionThatFailsIfValue1IsBad(value1);
          // ...
          result = success;
     }
     callback(result);
}

versus

void doSomething(type value1) {
     ResultType result = genericError;
     try {
          DoSomeActionThatFailsIfValue1IsBad(value1);
          // ...
          result = success;
     } catch (ExceptionType e) {
          result = specificError;
     } finally {
          callback(result);
     }
}

On the one hand, you're always doing a comparison. On the other hand, I honestly don't know what the internals of the system do to generate an exception, throw it, and then trigger the catch clause. It has the sound of being less efficient, but if it doesn't add overhead in the non-error case, then it's more efficient, on average. Which is it? Does it add similar checking anyway? Is that checking there in the implicit code added for exception handling, even with the additional layer of explicit checking? Perhaps it always depends on the type of exception? What am I not considering?

Let's also assume that all "bad values" are known -- that's an obvious issue. If you don't know all the bad values -- or the list is too long and not regular -- then exception handling may be the only way, anyway.

So, what are the pros and cons of each, and why?

Side questions to consider:

  • How does your answer change if the value is "bad" (would throw an exception) most of the time?
  • How much of this would depend on the specifics of the VM in use?
  • If this same question was asked for language-X, would the answer be different? (Which, more generally, is asking if it can be assumed checking values is always more efficient than relying on exception handling simply because it adds more overhead by current compilers/interpreters.)
  • (New) The act of throwing an exception is slow. Does entering a try block have overhead, even if an exception is not thrown?

Similarities on SO:

  • This is similar to the code sample in this answer, but states they are similar only in concept, not compiled reality.
  • The premise is similar to this question but, in my case, the requester of the task (e.g. "Something") isn't the caller of the method (e.g. "doSomething") (thus no returns).
  • And this one is very similar, but I didn't find an answer to my question.

  • And similar to far too many other questions to list, except:

    I'm not asking about theoretical best practice. I'm asking more about runtime performance and efficiency (which should mean, for specific cases, there are non-opinion answers), especially on resource limited platforms. For instance, if the only bad value was simply a null object, would it be better/more efficient to check for that or just attempt to use it and catch the exception?

11条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-01-23 05:28

In my opinion you should have try/catch blocks around anything that could potentially throw exceptions, if only to have a safe running system. You have finer control of error responses if you check for possible data errors fist. So I suggest doing both.

查看更多
可以哭但决不认输i
3楼-- · 2019-01-23 05:29

I could find surprisingly little current information about the cost of throwing Exceptions. Pretty obviously there must be some, you are creating an object, and probably getting stack trace information.

In the specific example you talk about:

if (value1 == badvalue || value1 == badvalue2 || ...) {
      result = specificError;
 } else {
      DoSomeActionThatFailsIfValue1IsBad(value1);
      // ...
      result = success;
 }

The problem for me here is that you are in danger if (probably incompletely) replicating logic in the caller that should be owned by the method you are calling.

Hence I would not perform those checks. Your code is not performing an experiment, it does "know" the data it's supposed to be sending down I suppose? Hence the likelyhood of the Exception being thrown should be low. Hence keep it simple, let the callee do the checks.

查看更多
贼婆χ
4楼-- · 2019-01-23 05:29

a question like this is like asking,

"is it more efficient to write an interface or a base class with all abstract functions"

does it matter which is more efficient? only one of them is the right way for a given situation

查看更多
再贱就再见
5楼-- · 2019-01-23 05:29

Note that if your code doesn't throw exceptions then it doesn't always imply that the input is within bounds. Relying on throwing exceptions by the standard Java (API + JVM), such as NullPointerException or ArrayIndexOutOfBoundsExceptions is a very unhealthy way to validate input. Garbage-in sometimes generates garbage-but-no-exception-out.

And yes, exceptions are quite expensive. They should not be thrown during a normal processing flow.

查看更多
一纸荒年 Trace。
6楼-- · 2019-01-23 05:35

Well, exceptions are more expensive, yes but for me, its about weighting the cost of efficiency vs bad design. unless your use case demands it, always stick to the best design.

the question really is, when do you throw an exception? in exceptional situations.

if your arguments are not in the range that you're looking for, i'd suggest returning an error code or a boolean.

for instance, a method,

public int IsAuthenticated(String username, String password)
{

     if(!Validated(username,password)
     {
          // just an error
          // log it
          return -2;   
     }

     // contacting the Database here
     if cannot connect to db
     {
          // woww this is HUUGE
          throw new DBException('cannot connect'); // or something like that
     }

     // validate against db here
     if validated, return 0;

    // etc etc

}

thats my 2 cents

查看更多
趁早两清
7楼-- · 2019-01-23 05:50

Optimizationally, I think you're going to find it's probably a wash. They'll both perform alright, I don't think exception throwing is ever going to be your bottleneck. You should probably be more concerned with what Java is designed to do (and what other Java programmers will expect) and that is thrown exceptions. Java is very much designed around throwing/catching exceptions and you can bet the designers made that process as efficient as possible.

I think it's mostly a philosophy and language culture sort of thing. In Java, the general accepted practice is that the method signature is a contract between your method and the code calling it. So if you receive an improper value, you generally throw an unchecked exception and let it be dealt with at a higher level:

public void setAge(int age)
{
    if(age < 0)
    {
        throw new IllegalArgumentException("Array can't be negative");
    }

    this.age = age;
}

In this case, the caller broke their end of the contract, so you spit their input back at them with an exception. The "throws" clause is for use when you can't fulfill your end of the contract for some reason.

public void readFile(String filename) throws IOException
{
   File myfile = new File(filename);
   FileInputStream fis = new FileInputStream(myfile);

   //do stuff

   fis.read();

   //do more stuff
}

In this case, as the method writer, you've broken your end of the contract because the user gave you valid input, but you couldn't complete their request due to an IOException.

Hope that kinda puts you on the right track. Good luck!

查看更多
登录 后发表回答