Java Labeled Continue: Control Nested Loops
Java labeled continue lets you skip to the next iteration of an outer loop from inside a nested loop. See syntax, examples, common pitfalls, and maintainability considerations.
Java's labeled continue statement lets you skip to the next iteration of an outer loop from inside a nested loop. You place a label before the loop and then reference that label in a continue statement. This gives you precise control over nested iteration without introducing extra flags or restructuring the surrounding code.
The Syntax of Labeled Continue
A label is any valid Java identifier followed by a colon, placed immediately before a loop statement. The labeled continue then uses that label after the continue keyword.
outer: for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { if (j == 2) { continue outer; } System.out.println("i=" + i + ", j=" + j); } }
In this example, when j equals 2, control jumps to the next iteration of the outer loop. The inner loop is not continued; instead, the outer loop increments i and starts a fresh inner loop. The output shows only j values 0 and 1 for each i.
How Labeled Continue Differs from Unlabeled Continue
An unlabeled continue only affects the innermost loop. It skips the rest of the current iteration and moves to the next iteration of that same loop. A labeled continue can skip to the next iteration of any enclosing loop, as long as the label is attached to that loop.
Consider this contrast:
for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (j == 1) { continue; // skips only the inner loop } System.out.println("i=" + i + ", j=" + j); } }
Here, the inner loop prints j=0 and j=2 for each i. The outer loop runs normally. With a labeled continue, the behavior changes because the outer loop advances immediately, potentially skipping multiple inner iterations.
A Practical Example: Skipping to the Next Outer Iteration
A common use case is searching for a condition across a two-dimensional array. Suppose you need to process each row but stop processing that row as soon as you find a negative value, then move to the next row.
int[][] matrix = { {1, 2, -1, 4}, {5, 6, 7, 8}, {-1, 10, 11, 12} }; rowLoop: for (int[] row : matrix) { for (int value : row) { if (value < 0) { System.out.println("Negative value found, skipping row"); continue rowLoop; } System.out.println("Processing " + value); } }
Without the label, you would need a boolean flag to break out of the inner loop and then check that flag in the outer loop to decide whether to continue. The labeled version keeps the logic inline and avoids an extra variable.
Using Labels with While and Do-While Loops
Labels work with while and do-while loops as well. The same rule applies: the label must be placed directly before the loop statement.
int i = 0; outerWhile: while (i < 3) { int j = 0; while (j < 3) { if (j == 2) { i++; continue outerWhile; } System.out.println("i=" + i + ", j=" + j); j++; } i++; }
In this example, when j reaches 2, the outer loop is continued after incrementing i. Note that the outer loop's increment is done manually because the continue bypasses the normal increment at the bottom of the while block. This is a common source of off-by-one errors when mixing labels with while loops.
Common Mistakes and Pitfalls
One frequent mistake is placing a label on the wrong statement. The label must immediately precede the loop you intend to target. Java allows labels before other statements, but continue only works when the label is attached to an enclosing loop (while, do, or for); trying to continue to a label on a block or an if statement will not compile.
Another pitfall is forgetting that continue with a label transfers control to the loop's update step. For a for loop, the update expression runs before the condition is checked. For a while loop, there is no automatic update; you must manage the loop variable before continuing, as shown above. This can lead to infinite loops if the variable is not updated before the continue.
A label's scope is the labeled statement itself. A continue can only reference a label attached to an enclosing loop within the same method. The compiler rejects references to labels that are not visible at that point.
Readability and Maintainability Considerations
Labeled continue is a control-flow construct. The biggest practical trade-off is usually readability. Overusing labels can make control flow harder to follow, especially when the labeled loop is far from the continue statement.
Maintainability improves when the label clearly communicates the loop's purpose. For example, rowLoop or searchLoop is more meaningful than a generic outer. However, if you find yourself using multiple labels in the same method, consider refactoring. Extracting the nested loops into a separate method that returns a boolean can often produce clearer code.
When to Prefer Alternatives Over Labeled Continue
Labeled continue is not always the best choice. If the condition that triggers the skip is complex or requires additional state, a dedicated method might be clearer. For example, you could extract the inner loop into a method that returns true when the outer loop should continue.
boolean shouldSkipRow(int[] row) { for (int value : row) { if (value < 0) { return true; } } return false; } for (int[] row : matrix) { if (shouldSkipRow(row)) { continue; } // process row }
This approach avoids labels entirely and makes the condition testable in isolation. Use labeled continue when the nested loop is short and the control flow is obvious. For deeper nesting or complex conditions, a method extraction or a flag-based approach often results in more maintainable code.
Another alternative is to use a labeled break, which exits the outer loop entirely rather than continuing it. The choice depends on whether you need to stop all iteration or just move to the next iteration of an outer loop. Understanding this distinction is key to writing correct nested-loop logic in Java.