Minimizing Condition Checking
September 6th, 2008
As I was grading the first project, I noticed a trend with a number of submissions regarding the efficiency of conditional blocks.
Take, for instance, the following code:
if (num == 0) {
// Do stuff
}
if (num == 1) {
// Do stuff
}
if (num == 2) {
// Do stuff
}
else {
// Do stuff
}
A typical way of evaluating a particular block of code is to look at the best and worst case scenarios. In other words, look at the inputs that should be the easiest to compute v. inputs that will cause the code to execute inefficiently.
We’ll start with the worst case. When num is any value other than 0 or 1, three conditions will be checked (one for each if block). None of these match, so we end up falling to the else code (more on why the else clause is incorrect later).
Now imagine the theoretical best case. If num is equal to 0, there should only be one condition checked, the first if statement. We know that, in that case, it is impossible that num would be equal to 1 or 2, therefore it doesn’t make sense to bother checking them.
Yet in the code above, they are checked. The net effect will still be the same, however this isn’t the most optimal way to write this logic. Furthermore, it lacks something in the way of readability (we’ll see why in a bit).
Now let’s look at an optimized version of the same logic:
if (num == 0) {
// Do stuff
}
else if (num == 1) {
// Do stuff
}
else if (num == 2) {
// Do stuff
}
else {
// Do stuff
}
In the second code block, the worst case scenario is the same; three conditions are checked when num is set to anything other than 0 or 1.
However, the best case is now greatly improved. If num is equal to 0, the first condition will evaluate to true and its code will be executed. Because the following conditions are marked as “else if” statements, they aren’t evaluated. An else clause, even if it is an else if, is only evaluated if the preceding if statement evaluates to false. Therefore, in the second code block, the best case scenario now only requires one condition to be matched.
Additionally, this code is more readable than the first block. These clauses are all linked in a way; specifically, they all check num for various values. When written as separate if statements, that relationship is lost. However when written using a chain of else ifs, the related nature of these checks is much more visible.
When discussing the first code block, I mentioned that the else clause was incorrect. An else clause only applies to the preceding if statement. In the first code block, the else is only evaluated if num is not equal to 2. While it will still execute correctly, it technically reads as incorrect as our intention was to have the else execute if none of the if conditions were matched.
In the second code block, however, the chaining of the else ifs provides the proper interpretation of our intended logic. The final else is only reached if all of the previous if conditions evaluate to false.
One last comment on this particular example. Given that the num variable is an int, an arguably more readable solution would be to use a switch statement. Keep in mind a switch statement can be used for any of the primitives (including char) as well as enums. Below is how the above code would look when implemented as a switch statement:
switch (num) {
case 0:
// Do stuff
break;
case 1:
// Do stuff
break;
case 2:
// Do stuff
break;
default:
// Do stuff
break;
}

