java-foundations Lesson 10 20 min read

Loop Patterns and Debugging

Fencepost, sentinel, accumulators, and the five common loop bugs

Reading: Reges & Stepp: Ch. 5 §5.2–5.4

After this lesson, you will be able to:

  • Apply the six essential loop patterns: counter, accumulator, product, max, min, and flag
  • Use do-while loops for input validation
  • Recognize and fix the five most common loop bugs
  • Debug loops with print statements and trace tables
  • Solve the fencepost problem with the “one outside, rest inside” pattern

The Pattern Behind Every Loop

Lesson 1.4 taught you for, while, and do-while syntax. But knowing how to write a loop is not the same as knowing what pattern to use inside it. Every practical loop follows one of six patterns — and once you recognize them, you will stop writing loops from scratch and start adapting templates.

From CSCD 110: In Python, you wrote count = 0 then count += 1 inside a loop. Java’s loop patterns work identically — initialize a variable, loop through data, update the variable each iteration. The pattern is universal; only the syntax changes.


The Six Essential Loop Patterns

Pattern 1: Counter

Count how many items satisfy a condition. Initialize to 0, increment by 1 when the condition is met:

int count = 0;
for (int i = 1; i <= 100; i++) {
    if (i % 7 == 0) {
        count++;
    }
}
System.out.println("Divisible by 7: " + count);  // 14

Pattern 2: Accumulator (Sum)

Maintain a running total. Initialize to 0, add the value each iteration:

int sum = 0;
for (int i = 1; i <= 100; i++) {
    if (i % 2 == 0) {
        sum += i;
    }
}
System.out.println("Sum of evens: " + sum);  // 2550

Pattern 3: Product Accumulator

Maintain a running product. Initialize to 1 (not 0!):

int product = 1;
for (int i = 1; i <= 6; i++) {
    product *= i;
}
System.out.println("6! = " + product);  // 720

Common Pitfall: Never initialize a product accumulator to 0. Anything times 0 is 0 — the result would always be 0. The identity for multiplication is 1, just as the identity for addition is 0.

Pattern 4: Find Maximum

Track the largest value seen. Initialize to Integer.MIN_VALUE:

int max = Integer.MIN_VALUE;
for (int i = 0; i < n; i++) {
    int value = scanner.nextInt();
    if (value > max) {
        max = value;
    }
}

Pattern 5: Find Minimum

Track the smallest value seen. Initialize to Integer.MAX_VALUE:

int min = Integer.MAX_VALUE;
for (int i = 0; i < n; i++) {
    int value = scanner.nextInt();
    if (value < min) {
        min = value;
    }
}

Pattern 6: Search / Flag

Determine whether any item meets a condition. Initialize a boolean to false:

boolean foundNegative = false;
for (int i = 0; i < n; i++) {
    int value = scanner.nextInt();
    if (value < 0) {
        foundNegative = true;
    }
}
if (foundNegative) {
    System.out.println("At least one negative number found.");
}

The ZOM-FMF Initialization Guide

How do you remember the correct starting value? Use ZOM-FMF:

Letter Initialize To Patterns
Zero 0 Counter, Accumulator (sum)
One 1 Product accumulator
MAX for min Integer.MAX_VALUE Find minimum
MIN for max Integer.MIN_VALUE Find maximum
First value first input Alternative to MAX/MIN
False false Search / flag
Check Your Understanding

A loop computes the factorial of n (the product of all integers from 1 to n). What should the accumulator be initialized to?


do-while for Input Validation

The do-while loop checks its condition after the body, guaranteeing at least one execution. This is perfect for input validation — you must prompt at least once:

Scanner scanner = new Scanner(System.in);
int score;

do {
    System.out.print("Enter score (0-100): ");
    score = scanner.nextInt();
    if (score < 0 || score > 100) {
        System.out.println("Invalid. Try again.");
    }
} while (score < 0 || score > 100);

System.out.println("Score accepted: " + score);
Loop Checks Condition Minimum Runs Best For
while Before body 0 Unknown iteration count
for Before body 0 Known iteration count
do-while After body 1 “Do first, then check”

Common Pitfall: Don’t forget the semicolon after while (condition); in a do-while. It is the only loop that ends with a semicolon.


The Fencepost Problem

Print the numbers 1 through 5 separated by commas: 1, 2, 3, 4, 5

A naive loop produces a trailing comma:

for (int i = 1; i <= 5; i++) {
    System.out.print(i + ", ");
}
// Output: 1, 2, 3, 4, 5,    ← trailing comma!

The fencepost pattern handles the first (or last) item outside the loop:

System.out.print(1);           // first post
for (int i = 2; i <= 5; i++) {
    System.out.print(", " + i);  // fence + post
}
// Output: 1, 2, 3, 4, 5     ← correct!

Key Insight: The name “fencepost” comes from the fact that a fence with N sections needs N+1 posts. When you have N items and N-1 separators, print one item outside the loop and loop the remaining N-1 items with separators.


The Five Most Common Loop Bugs

# Bug Symptom Fix
1 Off-by-one Loop runs one too many/few times Check < vs <=, start value
2 Infinite loop Program hangs Verify update moves toward termination
3 Wrong update Wrong values, but terminates Check sum += i vs sum = i
4 Wrong condition Stops too early or too late Read condition aloud
5 Wrong initialization First iteration wrong Use ZOM-FMF

Debugging with Print Statements

When a loop produces wrong output, add println inside the loop to watch variables change:

int sum = 0;
for (int i = 1; i <= 5; i++) {
    sum = i;    // BUG: should be sum += i
    System.out.println("DEBUG: i=" + i + " sum=" + sum);
}
System.out.println("Sum: " + sum);

Output reveals the bug — sum is being replaced each iteration instead of accumulated:

DEBUG: i=1 sum=1
DEBUG: i=2 sum=2
DEBUG: i=3 sum=3
DEBUG: i=4 sum=4
DEBUG: i=5 sum=5
Sum: 5

The Trick: Print debugging workflow: (1) Observe the wrong output. (2) Add println inside the loop showing key variables. (3) Compare actual values to expected values. (4) The first iteration where they diverge reveals the bug. (5) Remove debug prints after fixing.

Check Your Understanding

This code should print numbers 1 through 10, but prints 1 through 9. What is the bug?
for (int i = 1; i < 10; i++) { System.out.println(i); }


Complete Example: Statistics in One Pass

This program combines counter, accumulator, max, and min patterns in a single loop:

import java.util.Scanner;

public class Statistics {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("How many numbers? ");
        int n = scanner.nextInt();

        int count = 0;
        int sum = 0;
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;

        for (int i = 0; i < n; i++) {
            System.out.print("Enter number " + (i + 1) + ": ");
            int value = scanner.nextInt();

            count++;
            sum += value;
            if (value > max) { max = value; }
            if (value < min) { min = value; }
        }

        System.out.println("Count:   " + count);
        System.out.println("Sum:     " + sum);
        System.out.printf("Average: %.2f%n", (double) sum / count);
        System.out.println("Max:     " + max);
        System.out.println("Min:     " + min);
    }
}

Sample run:

How many numbers? 4
Enter number 1: 85
Enter number 2: 92
Enter number 3: 78
Enter number 4: 95
Count:   4
Sum:     350
Average: 87.50
Max:     95
Min:     78
Check Your Understanding

You want to print A-B-C-D-E (letters separated by dashes). Which approach correctly avoids a trailing dash?


Summary

Every loop follows one of six patterns: counter (count items), accumulator (running total), product (running multiplication), max (track largest), min (track smallest), and flag (search for a condition). Use ZOM-FMF to remember initial values.

The do-while loop guarantees at least one iteration — ideal for input validation where you must prompt before checking.

The fencepost problem arises when you need N items with N-1 separators. Solve it by handling the first (or last) item outside the loop.

The five common loop bugs are off-by-one, infinite loop, wrong update, wrong condition, and wrong initialization. Debug with print statements inside the loop to watch variables change.

Next lesson: Method design patterns — decomposition, overloading, and scope rules for building well-structured programs.