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 if
s and for
s 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:
- if and if-else statements
- switch statements
while
anddo-while
statementsfor
andenhanced for
statementsbreak
andcontinue
statements
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
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
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.