Java Flow Control: break and continue Statements

Introduction

Conditional statements and loops are a very important tool in programming. There aren't many things we could do with code that can only execute line-by-line.

That's what "flow control" means - guiding the execution of our program, instead of letting it execute line-by-line regardless of any internal or external factors. Every programming language supports some form of flow control, if not explicitly via ifs and fors or similar statements - then it implicitly gives us the tools to create such constructs, i.e. low-level programming languages usually achieve that affect with a lot of go-to commands.

Loops were a concept used long before computer programming was even a thing, but the first person to use a software loop was Ada Lovelace, commonly known by her maiden name - Byron, while calculating Bernoulli numbers, back in the 19th century.

In Java, there are several ways to control the flow of the code:

break

The break statement is one of Java's "jump statements", since it transfers the code execution to another part of code. We've already seen the break keyword used in the switch statement. We'll revisit it here, along with other use cases:

  • It can be used to stop execution of a switch statement case, instead of letting it continue executing code for following cases as well
  • It can be used to exit a loop before it has finished all its iterations, or as a form of exiting purposefully-created infinite loops
  • It can be used as an acceptable form of the "go-to" statement when we have multiple nested loops

The first two are relatively similar, since both are used to prematurely end the execution of one or more blocks of code:

// Finding at which index element a is in an array
int[] arr = {1,2,3,4,5,6};
int foundAt = -1;

int a = 4;

for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
    if (arr[i] == a) {
        foundAt = i;
        break;
    }
}
if (foundAt != -1)
    System.out.println(a + " was found at index " + foundAt);
else System.out.println(a + " wasn't found in the array");

Output:

1
2
3
4
4 was found at index 3

As we can see, the for loop executed as usual until it ran into the break statement, at which point Java stopped executing the loop and continued execution at the first line after the for loop.

Here's another example:

Scanner s = new Scanner(System.in);

while (true) {
    System.out.println("Do you wish to exit the infinite loop? Y/N");
    if (s.hasNext()) {
        String answer = s.next();
        if (answer.equals("Y") || answer.equals("y"))
            break;
    }
}

This loop will keep asking the user if he wishes to exit the loop until they either reply with the appropriate characters, where we exit the loop using break, or terminate the program with Ctrl + C.

By default the break statement only exits the innermost loop it's in.

If we wanted to find the first position at which a certain element can be found in a matrix, and we wanted to break out of the loops as soon as we found it (similar to the example with an array above), writing the following wouldn't work:

int[][] matrix = {{1,2,3},{4,5,6},{7,8,9}};

int foundAtI = -1;
int foundAtJ = -1;

int a = 4;

for (int i = 0; i < matrix.length; i++) {
    for (int j = 0; j < matrix[i].length; j++) {
        System.out.print(matrix[i][j] + " ");
        if (matrix[i][j] == a) {
            foundAtI = i;
            foundAtJ = j;
            break;
        }
    }
    System.out.println();
}

System.out.println();
if (foundAtI != -1)
    System.out.println(a + " was found at indices [" + foundAtI + "," + foundAtJ + "]");
else System.out.println(a + " wasn't found in the matrix");

Output:

1 2 3 
4 
7 8 9 
4 was found at indices [1,0]

We can see that the loop continued executing even after finding 4 in the matrix, after stuttering in the row in which 4 was located. This is because the break only exited the innermost loop, i.e. it stopped iterating through the current row and skipped to the next one. Furthermore, our task was to find the first occurrence of 4 in the matrix, and this way we'd return the last occurrence of 4 in our matrix.

This is where labeled statements come into play, which we'll take a look at next.

Labeled Statements

Labeled statements can be used together with the break or continue statements to simulate a go-to.

Labels can be applied to any code block between {}, for, for-each, while, do-while, if and switch statements, as well as expressions, assignments, return statements, try blocks, and throw statements. We then use them with the break and continue statements.

Labels work by applying an identifier to a construct:

Identifier : Construct

Such as:

someLoop: for (int i = 0; i < 100; i++) {}
someLabel: {
    int i = 10;
}

We can then call upon these labeled statements via a break or continue. For example, here we've labeled our outer loop as simply outer. In order to exit two or more nested loops, we break the outer loop by calling the label:

outer: for (int i = 0; i < matrix.length; i++) {
    for (int j = 0; j < matrix[i].length; j++) {
        System.out.print(matrix[i][j] + " ");
        if (matrix[i][j] == a) {
            foundAtI = i;
            foundAtJ = j;
            break outer;
        }
    }
    System.out.println();
}

Using this labeled loop in place of the loop in the example above yields the correct result and behavior:

1 2 3
4
4 was found at indices [1,0]

You can't use break label if the break isn't located within a block of code designated by that label or it will cause an error during compilation.

continue

The continue statement simply skips the rest of the current iteration and proceeds to the next one. It's useful when we want to skip entire (or part of) iterations that meet certain conditions, especially if they're performance heavy.

For a while loop, "skipping" the rest of the iteration means going straight back to checking the condition before the next iteration, and for a for loop this means going to the "step" part of the for loop (the part where we usually increment/decrement the control variable) and then checking the condition before the next iteration.

continue is usually used with an if statement -> we will continue if a certain condition is met. We use this if to execute the rest of the iteration only if the condition is not met, and skip using continue. For example

// We want to print every number from 1 to 20, except those divisible by 3
for (int i = 1; i <= 20; i++) {
    if (i % 3 == 0)
        continue;
    
    System.out.println(i);
}

The continue in our example could easily be avoided by using a slightly modified if statement, mainly by printing the number if i % 3 != 0 is true, otherwise doing nothing.

General advice regarding both break and (especially) continue is to try and use them mostly at the beginning of iterations as some form of preconditions that we're checking. Using a continue somewhere in the middle of the iteration is a great way to cause bugs that take you a while to figure out.

Of course, you can also use continue with labeled statements:

start: for (int i = 0; i < 10; i++) {
    System.out.println();
    for (int j = 0; j < 10; j++) {
        if (j >= i)
            continue start;
    }
    System.out.println("Since j will always be equal to or more than i, the 'start' loop will continue running indefinitely, and this piece of code will never run.");
}

Conclusion

Flow control in code is essential just about every application. Statements that alter the flow of code are fundamental building blocks and every aspiring developer should be completely in control/aware of how they work.

Using the break and continue statements, Java developers can simulate go-to statements and break out of certain loops if need be.