The Java Textbook

Part 2 · selection-and-iteration

Loops, and tracing them by hand

The exam asks what a loop prints and how many times its body runs. Both are answered by tracing, and tracing is a skill you practise.

By the end of this chapter you can

  • Trace a loop by hand and produce its output
  • Count how many times a loop body executes
  • Convert between for and while
  • Recognise the off-by-one and the infinite loop

Unit 2 is 25–35% of the exam, and loop questions dominate it. Almost all of them are one of two questions: what does this print, and how many times does the body run.

Neither rewards cleverness. Both reward writing the values down.

The three parts of a for

Which part runs when
public class Main {
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            System.out.println("body with i = " + i);
        }
        System.out.println("--- loop finished ---");
        // i is out of scope here: it belongs to the loop.
    }
}

The order is: initialise once, then repeatedly test, body, update.

The test happens before each body, including the first — so a for whose condition starts false runs zero times, which is a favourite question.

Zero iterations, and the boundary
public class Main {
    public static void main(String[] args) {
        int count = 0;
        for (int i = 5; i < 5; i++) count++;
        System.out.println("i < 5 starting at 5:  " + count + " iterations");

        count = 0;
        for (int i = 5; i <= 5; i++) count++;
        System.out.println("i <= 5 starting at 5: " + count + " iterations");

        count = 0;
        for (int i = 0; i < 5; i++) count++;
        System.out.println("0 to < 5:             " + count + " iterations");

        count = 0;
        for (int i = 1; i <= 5; i++) count++;
        System.out.println("1 to <= 5:            " + count + " iterations");
    }
}

Counting iterations. For for (int i = a; i < b; i++) the body runs $b - a$ times. For i <= b it runs $b - a + 1$. Getting that one wrong is the off-by-one, and it is worth deriving rather than memorising: list the values $i$ takes and count them.

Tracing

Write a column per variable and a row per iteration. Nothing else is reliable under exam conditions.

Trace this before you run it
public class Main {
    public static void main(String[] args) {
        int total = 0;
        for (int i = 1; i <= 4; i++) {
            total += i * i;
            System.out.println("i=" + i + "  i*i=" + (i*i) + "  total=" + total);
        }
        System.out.println("answer: " + total);
    }
}
$i$ $i^2$ total after
1 1 1
2 4 5
3 9 14
4 16 30

Four rows, no ambiguity. The temptation is to do it in your head and be wrong by one row.

Nested loops multiply

Counting the inner body
public class Main {
    public static void main(String[] args) {
        int inner = 0;
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 4; j++) {
                inner++;
            }
        }
        System.out.println("3 × 4 = " + inner);

        // A triangular nest: the inner bound depends on the outer variable.
        int tri = 0;
        StringBuilder shape = new StringBuilder();
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j <= i; j++) {
                tri++;
                shape.append("*");
            }
            shape.append("\n");
        }
        System.out.println("1+2+3+4 = " + tri);
        System.out.print(shape);
    }
}

Independent nests multiply: $3 \times 4 = 12$.

When the inner bound depends on the outer variable, they do not. j <= i gives $1 + 2 + 3 + 4 = 10$, a triangular number — and the exam asks this shape specifically because multiplying gives the wrong answer.

while, and the infinite loop

A for is a while with the three parts gathered up:

The same loop, both ways
public class Main {
    public static void main(String[] args) {
        StringBuilder a = new StringBuilder();
        for (int i = 0; i < 4; i++) a.append(i).append(" ");

        StringBuilder b = new StringBuilder();
        int i = 0;                 // initialise
        while (i < 4) {            // test
            b.append(i).append(" ");
            i++;                   // update — easy to forget
        }

        System.out.println("for:   " + a);
        System.out.println("while: " + b);
        System.out.println("same:  " + a.toString().equals(b.toString()));
    }
}

Use while when the number of iterations is not known in advance — reading until a sentinel, repeating until converged. Use for when it is.

break and continue

Stopping early and skipping ahead
public class Main {
    public static void main(String[] args) {
        int[] data = {4, 8, 15, 16, 23, 42};

        // break: stop the loop entirely
        for (int v : data) {
            if (v > 15) { System.out.println("first over 15: " + v); break; }
        }

        // continue: skip to the next iteration
        int oddSum = 0;
        for (int v : data) {
            if (v % 2 == 0) continue;
            oddSum += v;
        }
        System.out.println("sum of odds: " + oddSum);
    }
}

break exits the loop; continue skips the rest of this iteration and moves on. In nested loops, both affect only the innermost loop containing them — which is exactly what a tracing question will check.