8

This is purely a design question and the example is simple to illustrate what I am asking and there are too many permutations of more complex code to provide examples that would cover the topic. I am not referring specifically to this example, just using the example to illustrate what I am referring to.

In an if statement, is there a preferred way to order conditions, in terms of coding convention and, more importantly, efficiency?

For such a simple example, efficiency will not be an issue, but I am not asking for a code review per se, but an understanding of the concept.

Two alternatives:
1.

public double myStuff(int i)
{
    // check for an invalid number of tourists
    if (i > 0)
    {
        // do something here

        return doubleSomething;
    } else
    {
        return Double.NaN;
    }
}

2.

public double myStuff(int i)
{

    if (i <= 0)
    {
        return Double.NaN;

    } else
    {

        {
            // do something here

            return doubleSomething;
        }

If the if statement was to become more complex, and possibly nested, in what order should conditions be addressed?

gnat
  • 21,442
  • 29
  • 112
  • 288
  • 2
    One simple answer. `NO` The java compiler would compiler both of them into almost similar byte codes. – Prateek Nov 16 '13 at 00:29
  • 1
    Get a copy of *[Code Complete](http://cc2e.com/Default.aspx)*. Everyone should have this book. You will be glad you do. Steve McConnell does a great job of explaining just this sort of question .. "understand the concept". – radarbob Nov 16 '13 at 05:17
  • 1
    @Prateek there is no yes or no question, so a simple answer `NO` doesn't make sense. –  Nov 17 '13 at 01:54
  • @Skippy `In an if statement.. is there a preferred way to order conditions, in terms of coding convention and, more importantly, efficiency.` I think in this context `No` does make sense. – Prateek Nov 17 '13 at 02:02
  • @Prateek the question is `If the if statement was to become more complex, and possibly nested, in what order should conditions be addressed?` The rest is the blurb leading up to the question (hence no question mark.) anyway I'm not here to argue that.. if the answer is so simple, why not post an answer, rather than a glib comment –  Nov 17 '13 at 02:05
  • @Skippy I tend to answer questions only when I have time to answer them completely. My comment above is not a suitable candidate for an answer because it only gives you the answer not the reason. Your accepted answer also gives almost the same essence as my comment. So, I don't see any reason for me to add another duplicate answer just for the sake of answering. – Prateek Nov 17 '13 at 02:12
  • @Prateek btw I love your definition of study, can I please steal it?? –  Nov 17 '13 at 02:16
  • @Prateek in fact I am going to use it as my fb profile pic for a while cheers ;) –  Nov 17 '13 at 02:18
  • @Skippy Sure you can. It's free world :) – Prateek Nov 17 '13 at 02:22
  • @Prateek ah not with copyright and IP laws! ;) –  Nov 17 '13 at 02:23

3 Answers3

16

When it comes to large if blocks, optimization isn't really an issue. The compiler will generate pretty much the same result no matter how you organize them, assuming that the conditions are equally easy to calculate.

One technique I like to use is to use "guard statements" to check for invalid input right away, at the top of the method body. This way, the rest of the method can be formatted with the assumption that the arguments are already valid.

public double myStuff(int i, SomeOtherArgument widget) {
    // Perform any simple checks for invalid data right away and
    // fail fast if something smells bad.
    if (i <= 0)
        return Double.NaN;
    if (widget == null)
        throw new IllegalArgumentException();

    // If the stuff you've done above this line is trivial, there's no reason
    // to wrap the rest of the method body in a big else block. It's just noise.

    return doABunchOfWork();
}
Brant Bobby
  • 4,528
  • 4
  • 24
  • 22
  • 5
    +1 with a note: These are called "guard statements", and I use them all the time as well – Izkata Nov 16 '13 at 03:05
  • 3
    +1. Putting these guard statements at the start where they are easy for fellow coders to spot provides excellent documentation. – user949300 Nov 16 '13 at 06:34
  • I generalize this as follows: whenever you have a `return`/`break`/`continue` only in your `else` branch, invert the `if` so you don't need the `else` anymore. This can occur anywhere in the flow of code and would probably not always be called a "guard statement". – Marko Topolnik Nov 16 '13 at 08:26
  • These guard statements that provide an alternate exit point for the function can become a source of errors as the guard condition, exit code, and overall function complexity goes up. ideally each function should have only one exit point. It seems like a little return or two at the top will be understandable, but beware... – Andyz Smith Nov 17 '13 at 06:22
  • 2
    In my experience, using a single point of return in these cases is more error-prone than using guard clauses. – 17 of 26 Nov 21 '13 at 14:57
5

Efficiency is not usually a real concern for structuring if-statements, even if they are complex or nested. There are ways to nest if-statements that could be less efficient than others, but if the code is only run once and takes 5 nanoseconds too long, you aren't even going to be able to notice that there's a difference.

It is possible for this sort of thing to make a real difference in the running time of a program, especially if it's in the innermost loop of some nested loops. However, the best way to deal with a slow program is to measure it and find out where it's spending most of its time, so you can make that spot more efficient -- whether or not that spot has anything to do with nested if-statements.

What should be the concern for if-statements (especially nested or complex ones) is readability. It is very easy to make an unreadable mess out of complex nested if-statements, and the harder code is to read, the harder it is to ensure that it's correct or to debug it.

For simple if-statements, try to keep the condition from being too crazy. If you can't avoid a complex condition, try using formatting to clarify it, or use a helper function to evaluate the condition.

Mess:

if(a <= 5 && b > 10 && c < 4 && b <= 100
   && a > 2 && c > 1)

Better formatting:

if( (a > 2)  && (a <= 5)   &&
    (b > 10) && (b <= 100) &&
    (c > 1)  && (c < 4) )

Helper function:

if( withinLimits(a, b, c) )

Also, try to avoid double-negatives. "I wouldn't say that's not an incorrect statement" is hard to read. "That's wrong" is easy to read, and means the same thing. Single negatives aren't so bad, but sometimes you can make things a bit clearer by stating something positively instead.

For nested if-statements, it depends on how they're nested.

For something like this:

if(a) {
  if(b) {
    if(c) {
      something;
    }
  }
}

You could replace it with something like this:

if(a && b && c) {
  something;
}

If you have a complex nesting of if-statements, it's probably a mess. How to clean it up exactly depends on what exactly it is. It may be possible to create a decision table -- an array that has indices that represent what we're basing the decision on and entries that are the resulting decision. If so, we can just look up the answer without using any if-statements at all.

Michael Shaw
  • 5,116
  • 1
  • 21
  • 27
0

For composite conditions, you could consider to take into account the fact that, in Java, conditions evaluation is lazy. That means that the program will only evaluate the (sub)conditions until it can decide if the conditional statements must be executed or not.

So, for instance, if you have two conditions c1 and c2, and if c1 can be quickly evaluated since c2 take a while to be evaluated, prefer if(c1 and/or c2) to if(c2 and/or c1), so that you could more quickly determine if the global condition is respected or not. Similarly, prefer

if(c1){
   if(c2){
      (...)
   }
}

to

if(c2){
   if(c1){
      (...)
   }
}

In the worse case it does not change anything, and in the best case you spare a test.

This is a general principle, you can also consider the fact that one of the conditions will be true virtually all the times. In that case, it could be interesting to place it first in a OR composition, so that you will virtually never test the second condition. It becomes harder to decide how to order the conditions when you have to consider simultaneously time consumption and value probability.

Be aware that before changing the order of your conditions, you must be sure they imply no side effect, otherwise a reordering may change the behaviour of your program.

mgoeminne
  • 1,158
  • 6
  • 11
  • Unless these tests take a long long time (e.g., they involve calculating some matrix decomposition or reading from a database), or they get called a gazillion times in a loop, optimizing `if()` statements for speed is foolhardy. – user949300 Nov 16 '13 at 17:20
  • I don't think it's foolhardy, in the worse case you spend time for no perceptible speed improvement. But, indeed, generally tests are not so heterogeneous that a such "optimisation" is interesting. – mgoeminne Nov 18 '13 at 07:30