21

I have 2 fors, after the nested for I have some code which I don't want to execute if a condition is true inside the nested for. If I use break that code would execute, so (as I learned in SCJP) I used continue label; for the outer for. Is this a deprecated usage of Java ? Old fashioned ? Somebody suggested to use recursion or something else, but for me this is perfectly normal, simple, up-to-date and the perfect way of doing it.

here:
for (bla bla) {
   for (bla bla) {
      if (whatever) continue here;
   }
// some code I don't want to execute if whatever is true
}

Thanks

Edited:
If I rephrase my question as: How can you 'navigate' between multiple nested fors ? This approach would be the 'recommended' way ? because this is what it says in SCJP Book. If not .. would this mean that Katherine Sierra and Bert Bates are wrong ?

Edited2:
Why is continue label; discouraged ? I want an answer of the concepts or inside workings of OOP or Java, what might go wrong ..

Zaheer Ahmed
  • 28,160
  • 11
  • 74
  • 110
Cosmin Cosmin
  • 1,526
  • 1
  • 16
  • 34
  • 2
    If you NEED to navigate between multiple nested for loops then it is not only the recommended way but the only way. So they are right. Although I would recommend avoiding navigating between multiple nested for loops. – Mikael Vandmo Jul 11 '11 at 13:09

6 Answers6

10

The answer is: it depends. If you find yourself using continue a lot then it might be a sign that your code needs a refactor. However, in the scenario you've given it seems like an OK place to use it.

Mark Pope
  • 11,244
  • 10
  • 49
  • 59
8

I would refactor to make it more readable.

Example:

if (!checkWhatever()) {
    // some code I don't want to execute if whatever is true
}
boolean checkWhatever() {
    for (bla bla) {
       for (bla bla) {
          if (whatever) return false;
       }
    }
    return true;
}
Mikael Vandmo
  • 887
  • 8
  • 14
  • and if you have 5 nesting levels ? This solution would be ok for a particular case. I want a generalization. – Cosmin Cosmin Jul 11 '11 at 12:52
  • 5
    @Cosmin Vacaroiu 5 nested loops? Sounds like a design problem, and should be refactored. – Kaj Jul 11 '11 at 13:02
  • 8
    There is no general way to to replace the "continue to label". On the other hand there is always some way to replace it. – Mikael Vandmo Jul 11 '11 at 13:06
  • 1
    if you have Object[][][][][] what would you do so that it won't be bad design ? – Cosmin Cosmin Jul 11 '11 at 13:06
  • Encapsulate Object[][][][][] in a class and end up with something like List. – Mikael Vandmo Jul 11 '11 at 13:12
  • 9
    @Cosmin, I would say, don't have `Object[][][][][][]` (unless you have a 5-dimensional physics problem. ;) At some point it makes sense to use a Class to wrap the `[]` or `[][]`. – Peter Lawrey Jul 11 '11 at 13:12
  • @Cosmin: the "generalization" is the design principle that each chunk of code should be simple enough to understand immediately. 5 nested loops is almost certainly not that simple. The solution is to place the innermost parts of the method into one or more separate methods, such that the outer control flow is easy to read: keep the top level method simple, hide detail in other method(s). This allows a reader to grasp your solution top-down: the big picture, then the details. – ToolmakerSteve Nov 21 '13 at 05:10
5

I would say its discouraged. It still has valid uses where alternatives are more complex or error prone (i.e. not an improvement)

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • +1 even though I don't think break/continue to a label discouraged. The smell is usually, byt not always, the nested loops. – Kaj Jul 11 '11 at 13:03
  • 1
    @Kaj. Somewhat disagree. Double nested loop, if simple enough, doesn't smell much. The need for a LABELED continue increases the smell - time to consider refactoring - moving inner loop to its own method. Cosmin: If the code is as simple as your example, then yes, KISS. If the method gets more complex, break out inner detail, so that outer structure is readable at a glance. And eliminate need for labeled continue as part of that readability. – ToolmakerSteve Nov 21 '13 at 05:03
4

With reference to your Edit 2, it is always going to look a bit iffy because it violates an older programming orthodoxy than OO: 'structured programming'. It smacks of goto as well, and all good programmers know they need to go to confession if they let a goto into their code.

There could be some concern that it might make it harder for a compiler to analyse the control flow of a function, but it is the sort of tool that typically gets used for efficiency reasons. For instance, the Apache implementation of java.lang.String uses it in this function that is at least intended to be an optimisation:

/*
 * An implementation of a String.indexOf that is supposed to perform
 * substantially better than the default algorithm if the "needle" (the
 * subString being searched for) is a constant string.
 *
 * For example, a JIT, upon encountering a call to String.indexOf(String),
 * where the needle is a constant string, may compute the values cache, md2
 * and lastChar, and change the call to the following method.
 */
@SuppressWarnings("unused")
private static int indexOf(String haystackString, String needleString,
        int cache, int md2, char lastChar) {
    char[] haystack = haystackString.value;
    int haystackOffset = haystackString.offset;
    int haystackLength = haystackString.count;
    char[] needle = needleString.value;
    int needleOffset = needleString.offset;
    int needleLength = needleString.count;
    int needleLengthMinus1 = needleLength - 1;
    int haystackEnd = haystackOffset + haystackLength;
    // Label: <----------------------------------------
    outer_loop: for (int i = haystackOffset + needleLengthMinus1; i < haystackEnd;) {
        if (lastChar == haystack[i]) {
            for (int j = 0; j < needleLengthMinus1; ++j) {
                if (needle[j + needleOffset] != haystack[i + j
                        - needleLengthMinus1]) {
                    int skip = 1;
                    if ((cache & (1 << haystack[i])) == 0) {
                        skip += j;
                    }
                    i += Math.max(md2, skip);
                    // Continue to label:  <---------------------------
                    continue outer_loop;
                }
            }
            return i - needleLengthMinus1 - haystackOffset;
        }

        if ((cache & (1 << haystack[i])) == 0) {
            i += needleLengthMinus1;
        }
        i++;
    }
    return -1;
}
ahcox
  • 9,349
  • 5
  • 33
  • 38
1

Refactor to make it more readable, by placing the inner loop in its own method:

for (bla bla) {   
  DoStuff();
}
void DoStuff() {
  for (bla bla) {
    if (whatever) return;
  }
  // some code to execute when whatever is false.
}

The principle: If a method becomes complex enough to require LABELING a block, consider refactoring part of that method into a separate method, such that no label is needed.

Similarly, it is unwise to make methods that are THREE loops deep. Unless the loops are very simple. Even if no labels are needed. Make sure the outermost flow construct (a loop, or an if/else, or a switch) is easy to read, by hiding complexity inside other methods. Even if those methods are only called from one place.

ToolmakerSteve
  • 18,547
  • 14
  • 94
  • 196
  • @Mikael has the right idea; however his refactoring is incorrect -- it does not preserve the behavior of the original -- "// some code" is part of the inner loop, not the outer loop. I proposed this change as an edit to his post, along with my explanatory paragraphs, but that was rejected by reviewers as too major a change / changes meaning of post. So I post here as a separate answer. – ToolmakerSteve Nov 27 '13 at 21:34
0

Use a boolean called 'success' or something like that. It's much easier to read and to follow the flow. Gotos should only be used for error handling.

boolean success = true;
for(int outer = 0; (outer <= outerLimit) && sucess; outer++)
{
    for(int inner = 0; (inner <= innerLimit) && success; inner++)
    {
        if( !doInnerStuff() )
        {
            success = false;
        }
    }

    if( success )
    {
        success = doOuterStuff();
    }
}
Evvo
  • 481
  • 5
  • 8
  • you probably want to either `break` after `success = false` or add a condition to make sure you exit the inner loop. – njzk2 Jul 17 '14 at 17:14