Concepts / Loop Control Variables

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.

  • Programming

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.

java
Output
Ada
Grace
Linus
Notes on the code
  • 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
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.

Formula, step by step
for ( T x : coll ) { body }
  1. 1
    T is the declared type of the loop control variable, and it must match the type of the objects in coll
  2. 2
    x is the loop control variable, the name the current object is given inside the body
  3. 3
    : is read as in or each item of, separating the variable from the collection
  4. 4
    coll is the collection being visited, which can be any collection, not only an array or an ArrayList
  5. 5
    The 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.

Pass 1name holds AdaPass 2name holds GracePass 3name holds LinusStopno objects remainnext objectnext objectcollection empty
On each pass of for (String name : namelist), which object does name hold, and when does the loop stop?

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

Worked example

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.

  1. 1
    Check 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.

  2. 2
    Trace the first pass

    On pass 1, name holds Ada. The body prints Ada, which matches the expectation.

  3. 3
    Trace 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.

  4. 4
    Trace 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.

Result

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.

MEDIUM

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

Definition

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.

java
Output
1
2
3
4
5
6
7
8
9
10
Notes on the code
  • 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.

java
Output
5
4
3
2
1
Liftoff
Notes on the code
  • 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.

i = 1processedi = 1processedi = 2processedi = 2processedi = 3processedi = 3processedi = 4processedi = 4processedi = 5processedi = 5not processed
For a counting loop that starts at 1 and runs up to 5, which values are processed when the condition uses <= and when it uses <?

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.

Before
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.

After
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 like
    for (int i = 1; i < 5; i++) is meant to print 1 through 5
    Why it fails

    The condition stops the loop before i reaches 5, so the last value is dropped.

    Do this instead

    Use <= 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 like

    Switching between < and <= at random until the output looks right

    Why it fails

    The operator is only meaningful relative to the intended range, so guessing hides the real bound problem.

    Do this instead

    Decide 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.

Code walkthrough
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);
        }
    }
}
  1. 1
    Before the first pass, i is set to 0 and j is set to 5 by the initialization
  2. 2
    The condition i < 5 is tested, and it is true, so the body runs with i=0 and j=5
  3. 3
    After the body, both update expressions run: i becomes 1 and j becomes 4
  4. 4
    The 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
Output
i=0 j=5
i=1 j=4
i=2 j=3
i=3 j=2
i=4 j=1

What 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
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

EASY

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

  1. 1
    A for-each loop, written for ( T x : coll ), visits every object in a collection, and it works with any collection
  2. 2
    The loop control variable receives each object in turn, and its declared type must match the type of those objects
  3. 3
    A counting loop processes a range of integers through a loop control variable, and it can count upward or downward
  4. 4
    The 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.