Loop Control Variables
A for-each loop, written for ( T x : coll ), visits every object in a collection, and it can be used with any collection rather than only arrays or ArrayLists.
A Loop That Follows the Collection
Many programs need to do the same thing to every item in a group: print each name, add up each price, check each student record. A loop is the tool for repeating work, and the loop control variable is the name that stands for the current item while the loop body runs. This article follows one idea through several forms. In each form, the question is the same: on each pass, what value does the loop control variable hold, and when does the loop stop?
Before reading the explanation, look at a small program built around one collection of names. The program prints every name, one name per line. Form a picture of what the loop does before reading on, because the explanation will be easier to follow once you have seen the code run.
Ada
Grace
Linus- namelist is a collection whose objects are Strings
- name is the loop control variable
- The body runs once for each object in namelist
What do you think happens?
Before looking at the output, what does the loop print, and in what order?
Reveal answer
It prints Ada, Grace, and Linus, one per line, in the same order the names were added to namelist.
The loop visits the objects in the collection in order. On each pass, name holds the next object, and the body prints it.
The Three Parts of the For-Each Header
The general form of a for-each loop is a header with three parts, followed by a body in braces. Each part has a specific job, and the most common error comes from mixing them up. The header reads roughly as: for each object x of type T in the collection coll, process x.
for ( T x : coll ) { body }- 1T is the declared type of the loop control variable, and it must match the type of the objects in coll
- 2x is the loop control variable, the name the current object is given inside the body
- 3: is read as in or each item of, separating the variable from the collection
- 4coll is the collection being visited, which can be any collection, not only an array or an
ArrayList - 5The body in braces runs once for each object in coll, with x holding that object
Notice what the header does not contain. There is no counter, no starting position, and no test that compares a position against a size. The loop gives you the objects themselves. If you need to know where an object sits in the collection, that is a different question, and this loop form is not built to answer it. That absence is the reason the form is easy to read when all you need is to visit each object.
How the Loop Control Variable Receives Each Object
Each pass through a for-each loop follows the same cycle. The loop first checks whether any object remains in the collection. If one does, it assigns that object to the loop control variable, runs the body with that value, and then returns to the check. When no objects remain, control leaves the loop. The number of passes is therefore set by the collection, not by a counter written in the header.
The decision point is the only place where the loop chooses between continuing and stopping. Everything inside the body runs only after an object has been assigned. So any surprise in behavior must come from one of two places: the object assigned on that pass, or the work the body did with it. Keeping that split in mind makes problems much easier to locate.
Why the Declared Type Must Match the Objects
The loop control variable x is declared with the element type T. Its job is to receive each object in turn, so the declared type has to describe those objects. In the name-printing program, the collection holds Strings, and the loop control variable is declared as String. The declared type and the element type agree, and each object can be handed to the variable without any conversion.
Tracing a For-Each Loop Pass by Pass
Finding where a for-each result goes wrong
A program is meant to print the names in namelist, but it prints a name that was not expected. The collection holds Ada, Grace, and Linus. Find the pass on which the output first diverges from what was expected.
- 1Check the header first
The header for (String name : namelist) names the collection and the variable. Both look correct, so the cause must be the object name holds on a particular pass or the work the body does with it.
- 2Trace the first pass
On pass 1, name holds Ada. The body prints Ada, which matches the expectation.
- 3Trace the second pass
On pass 2, name holds Grace. If the output shows Grace here, the loop is behaving correctly and the earlier expectation was wrong.
- 4Trace the third pass
On pass 3, name holds Linus. If the body printed something other than name, such as a fixed string, the divergence comes from the body rather than the header.
The divergence is found by asking which object the loop control variable held on each pass. The first pass where the printed value and the held object disagree is the place to look.
A loop reads for (int score : scores) over a collection of Integer objects, and the body prints score. Suppose the output shows three numbers, but the collection contains five. Which part of the header would you check first, and what would you record for each pass to find where the output stops matching the collection?
Hints
- Start with the collection named in the header and confirm which objects it holds
- On each pass, write down the value the loop control variable holds before looking at the output
Counting Loops and Their Control Variable
A counting loop processes a range of integer values through a loop control variable. Unlike a for-each loop, the values are produced by the loop itself, not taken from a collection. The loop control variable is the integer that takes on each value in the range, and the body can use it directly.
1
2
3
4
5
6
7
8
9
10- n is the loop control variable
- The initialization sets n to 1
- The condition n <= 10 is tested before each pass
- The update n++ runs after each pass
The three header parts here do different jobs. The initialization runs once, before the first pass. The condition is checked before each pass, and the loop continues only while it is true. The update runs after each pass. Because the condition uses <=, the value 10 is processed, and the sequence is inclusive of the maximum.
Counting Downward
A counting loop does not have to increase. To count downward, start the loop control variable at the larger value, decrement it with the -- operator, and continue while it remains greater than or equal to the lower bound. Each of the three header parts changes in a predictable way.
5
4
3
2
1
Liftoff- The initialization starts n at the larger value, 5
- The condition n >= 1 keeps the lower boundary 1 in the loop
- The update n-- moves the variable downward by one each pass
Compare the two forms. Counting up needs a smaller starting value, an update that increases the variable, and a condition that bounds it from above. Counting down reverses all three: the start is larger, the update decreases, and the condition bounds the variable from below. Counting down requires a condition that preserves the lower boundary when that boundary should be processed.
Choosing Between < and <=
The choice between < and <= determines whether a boundary value is processed, so it must match the intended range. A common belief among beginners is that changing <= to < is a small syntactic change. It can change which values the loop processes, especially the boundary value. The key question is not which operator looks more familiar. The key question is whether the final value belongs in the loop's work.
The two columns show the difference. With <=, the value 5 is processed and the loop covers the full range from 1 to 5. With <, the loop stops before 5, so the final value is dropped. Neither choice is inherently correct. The correct operator is the one that matches whether the final value should be part of the work.
Off-by-One Errors in Loop Bounds
An off-by-one error happens when a loop starts or stops one position away from where the intended range begins or ends. It is usually a bound that is slightly wrong, not a broken loop structure, so the loop runs without complaint but processes one value too few or one too many. The fix is to decide, for the range you want, which value the loop should start on and which value is the last one it should process, and then to write the operator and the bound to match.
int n = 5;
for (int i = 1; i < n; i++) {
System.out.println(i);
}Prints 1, 2, 3, 4. The value 5 is never processed.
int n = 5;
for (int i = 1; i <= n; i++) {
System.out.println(i);
}Prints 1, 2, 3, 4, 5. The final value is now included.
Common mistake Using < when the final value should be processed
What it looks likefor (int i = 1; i < 5; i++) is meant to print 1 through 5Why it failsThe condition stops the loop before i reaches 5, so the last value is dropped.
Do this insteadUse <= 5 so that the value 5 is part of the loop's work.
Common mistake Changing the operator without deciding what the final value should be
What it looks likeSwitching between < and <= at random until the output looks right
Why it failsThe operator is only meaningful relative to the intended range, so guessing hides the real bound problem.
Do this insteadDecide first whether the final value belongs in the work, then choose the operator that matches.
Common mistake Counting downward with a bound that drops the lower boundary
Multiple Variables in One For Header
A for loop can initialize and update more than one variable, which lets two values change together in one header. In the pattern below, i controls the condition, while both i and j are initialized and updated inside the for statement. The body prints both values, so each pass displays a paired upward and downward count. Read the header by asking which part runs once, which part runs before each pass, and which part runs after each pass.
public class PairedCount {
public static void main(String[] args) {
for (int i = 0, j = 5; i < 5; i++, j--) {
System.out.println("i=" + i + " j=" + j);
}
}
}- 1Before the first pass, i is set to 0 and j is set to 5 by the initialization
- 2The condition i < 5 is tested, and it is true, so the body runs with i=0 and j=5
- 3After the body, both update expressions run: i becomes 1 and j becomes 4
- 4The loop repeats until i reaches 5, which fails the condition, so the loop stops
- The initialization declares i and j together and sets them once
- The condition checks only i, so the loop ends when i reaches 5
- The update increments i and decrements j after each pass
i=0 j=5
i=1 j=4
i=2 j=3
i=3 j=2
i=4 j=1What do you think happens?
In the paired count above, what is the value of j on the third pass, where i is 2?
Reveal answer
j is 3 on the third pass.
j starts at 5 and is decremented after each pass. After two updates, j is 3, and i is 2 at the same time, so the printed line is i=2 j=3.
Practice Tracing Loop Control Variables
Write a for-each loop that prints every String in a collection named cities, using a loop control variable named city. Then write a counting loop that prints the numbers from 10 down to 1 inclusive, using a loop control variable named n. Finally, state which of your two loops would need an explicit bound, and why.
Hints
- The for-each header needs only the declared type, the variable name, the colon, and the collection
- For the countdown, the start is 10, the update decrements, and the condition must keep 1 in the loop
Summary of Loop Control Variables
- 1A for-each loop, written for ( T x : coll ), visits every object in a collection, and it works with any collection
- 2The loop control variable receives each object in turn, and its declared type must match the type of those objects
- 3A counting loop processes a range of integers through a loop control variable, and it can count upward or downward
- 4The choice between < and <= depends on whether the final value belongs in the loop's work, and off-by-one errors come from bounds that are one position away from the intended range
At a glance
- A for-each loop, written for ( T x : coll ), visits every object in a collection, and the number of passes is set by the collection.
- The loop control variable receives each object in turn, and its declared type must match the type of those objects.
- A counting loop uses an integer loop control variable with an initialization, a condition, and an update, and it can count upward or downward.
- The operator in the condition decides whether the final value is processed, and off-by-one errors come from bounds that are one position away from the intended range.