Check Dont Catch

This is a performance JavaIdiom.

It is often more efficient in Java to check to see if an exceptional condition exists than to let an exception be raised and have to catch it afterwards.

For example, a loop such as this

   try {
     for (int i=0; ; i++) {
       // do something with someArray here
     }
   } catch (ArrayIndexOutOfBoundsException ex) {
     // ignore the exception
   }

can be more efficiently written as

   for (int i=0; i < someArray.length; i++) {
     // do something with someArray here
   }

This is because raising an Exception is not a no-cost operation. Raising an Exception is a very expensive exercise intend to deal with exceptional occasions (when performance isn't usually the major issue anyway). Using Exceptions as standard flow-control is very, very bad.

(Actually, for large lists the first method may well be faster - have tested this - since you're is doing the in-bounds check and you're not replicating it. The overhead of the exception throw is outweighed by the time saving for each iteration. But it's VERY BAD!)


Instead of null as return value use the NullObject pattern, or if you can't do so throw a RuntimeException. If you throw an exception you should provide a possibility to check if the critical method can be executed without causing an exception. Hence..

	try {
		obj.get(foo);
	}
	catch (RuntimeException e) { ... }

..should be rewritten as..

	if (obj.contains(foo)) {
		// we are now sure that get(foo) doesn't throw an exception
		obj.get(foo);
	}

If this needs to be ThreadSafe you'll need to lock obj before contains() and release the lock after get()

The above is copied from NoNullBeyondMethodScope.

It is my understanding that contains() in almost any collection class is usually only slightly faster than get(). So the second example is essentially calling get() twice. Is a try catch block so time cost prohibitive that calling get() twice is still faster?

Also, if a particular collection implementation has already synchronized get() then locking on obj before contains() and releasing after get() will double lock obj, a situation which I was under the impression one should avoid when possible. If the collection is not ThreadSafe then even if you lock obj, the remove() method will not be synchronized and contains() could return true and get() could result in an exception. It is probably unlikely, but it is not still possible? So then to be really sure you won't get a NullPointerException you'd have to lock obj every time you try to access it. And to cover the double lock case mentioned above, you should probably introduce a new object and lock on that instead.

The following 4 cases were tested in Java 1.5.

	The naive, just get() it and let a NullPointerException generate a stack trace
	The try-catch block around get()
	The unsynchronized contains() then get()
	The synchronized constains() then get()
The test did not ever try to get() an errant key. So this test is attempting to measure "good" behavior.

The results were in the listed order from fastest to slowest for a million element HashMap. Surprisingly enough they were in the same order for a thousand element HashMap, and in fact were the same even for a 10 element HashMap.

So if try-catch is faster, and it avoids the unnecessary complications and potential pitfalls of the contains() then get() route, is it not wiser to CatchDontCheck instead of CheckDontCatch?

Try-catch indicates that we are about to try something that might not work, if indicates that we have a condition that must be met before we will do something. For the particular example from NoNullBeyondMethodScope, doesn't try-catch capture the intention of our code better than the contains() then get()?

These are serious questions, not meant to be rhetorical. If I can convince the Wiki, then I might try convincing my co-workers.

--MikeDoberenz?


See CatchDontCheck, CatchDontCheckRefuted, BetterForLoopConstruct.


CategoryJava

CategoryException


EditText of this page (last edited June 11, 2005)
FindPage by browsing or searching

This page mirrored in JavaIdioms as of April 29, 2006