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