List
or a Set
. For the exam, you should know that this does not include all of the Collections Framework classes or interfaces, but only those that implement or extend that Collection
interface. For example, Map
is not supported in a for-each loop, although Map
does include methods that return Collection
instances.
The left side of the for-each loop must include a declaration for an instance of a variable whose type is compatible with the type of the array or collection on the right side of the statement. On each iteration of the loop, the named variable on the left side of the statement is assigned a new value from the array or collection on the right side of the statement.
Compare these two methods that both print the values of an array, one using a traditional for
loop and the other using a for-each loop:
public void printNames(String[] names) { for(int counter=0; counter<names.length; counter++) System.out.println(names[counter]); } public void printNames(String[] names) { for(var name : names) System.out.println(name); }
The for-each loop is a lot shorter, isn't it? We no longer have a counter
loop variable that we need to create, increment, and monitor. Like using a for
loop in place of a while
loop, for-each loops are meant to reduce boilerplate code, making code easier to read/write, and freeing you to focus on the parts of your code that really matter.
We can also use a for-each loop on a List
, since it implements Iterable
.
public void printNames(List<String> names) { for(var name : names) System.out.println(name); }
We cover generics in detail in Chapter 9, “Collections and Generics.” For this chapter, you just need to know that on each iteration, a for-each loop assigns a variable with the same type as the generic argument. In this case, name
is of type String
.
So far, so good. What about the following examples?
String birds = "Jay"; for(String bird : birds) // DOES NOT COMPILE System.out.print(bird + " "); String[] sloths = new String[3]; for(int sloth : sloths) // DOES NOT COMPILE System.out.print(sloth + " ");
The first for-each loop does not compile because String
cannot be used on the right side of the statement. While a String
may represent a list of characters, it has to actually be an array or implement Iterable
. The second example does not compile because the loop type on the left side of the statement is int
and doesn't match the expected type of String
.
Controlling Flow with Branching
The final types of control flow structures we cover in this chapter are branching statements. Up to now, we have been dealing with single loops that ended only when their boolean
expression evaluated to false
. We now show you other ways loops could end, or branch, and you see that the path taken during runtime may not be as straightforward as in the previous examples.
Nested Loops
Before we move into branching statements, we need to introduce the concept of nested loops. A nested loop is a loop that contains another loop, including while
, do
/while
, for
, and for-each loops. For example, consider the following code that iterates over a two-dimensional array, which is an array that contains other arrays as its members. We cover multidimensional arrays in detail in Chapter 4, “Core APIs,” but for now, assume the following is how you would declare a two-dimensional array:
int[][] myComplexArray = {{5,2,1,3},{3,9,8,9},{5,7,12,7}}; for(int[] mySimpleArray : myComplexArray) { for(int i=0; i<mySimpleArray.length; i++) { System.out.print(mySimpleArray[i]+"\t"); } System.out.println(); }
Notice that we intentionally mix a for
loop and a for-each loop in this example. The outer loop will execute a total of three times. Each time the outer loop executes, the inner loop is executed four times. When we execute this code, we see the following output:
5 2 1 3 3 9 8 9 5 7 12 7
Nested loops can include while
and do
/while
, as shown in this example. See whether you can determine what this code will output:
int hungryHippopotamus = 8; while(hungryHippopotamus>0) { do { hungryHippopotamus -= 2; } while (hungryHippopotamus>5); hungryHippopotamus--; System.out.print(hungryHippopotamus+", "); }
The first time this loop executes, the inner loop repeats until the value of hungryHippopotamus
is 4
. The value will then be decremented to 3
, and that will be the output at the end of the first iteration of the outer loop.
On the second iteration of the outer loop, the inner do
/while
will be executed once, even though hungryHippopotamus
is already not greater than 5
. As you may recall, do
/while
statements always execute the body at least once. This will reduce the value to 1
, which will be further lowered by the decrement operator in the outer loop to 0
. Once the value reaches 0
, the outer loop will terminate. The result is that the code will output the following:
3, 0,
The examples in the rest of this section include many nested loops. You will also encounter nested loops on the exam, so the more practice you have with them, the more prepared you will be.
Adding Optional Labels
One thing we intentionally skipped when we presented if
statements, switch
statements, and loops is that they can all have optional labels. A label is an optional pointer to the head of a statement that allows the application flow to jump to it or break from it. It is a single identifier that is followed by a colon (:
). For example, we can add optional labels to one of the previous examples:
int[][] myComplexArray = {{5,2,1,3},{3,9,8,9},{5,7,12,7}}; OUTER_LOOP: for(int[] mySimpleArray : myComplexArray) { INNER_LOOP: for(int i=0; i<mySimpleArray.length; i++) { System.out.print(mySimpleArray[i]+"\t"); } System.out.println(); }
Labels follow the same rules for formatting as identifiers. For readability, they are commonly expressed using uppercase letters in snake_case with underscores between words. When dealing with only one loop, labels do not add any value, but as you learn in the next section, they are extremely useful in nested structures.
int frog = 15; BAD_IDEA: if(frog>10) EVEN_WORSE_IDEA: { frog++; }
The break Statement
As you saw when working with switch
statements, a break statement transfers the flow of control out to the enclosing statement. The same holds true for a break
statement that appears inside of a while
, do
/while
, or for
loop, as it will end the loop early, as shown in Figure 3.9.