☕ Java

while Loop

The while loop is a condition-controlled loop — it continues executing as long as its boolean condition remains true, without specifying in advance how many iterations will occur. It is the natural choice for reading input until end-of-stream, polling until a condition changes, retrying an operation, or any scenario where the termination condition depends on data or events that cannot be predetermined. This entry covers syntax, execution flow, common patterns, infinite loops with controlled exit, and the differences between while and for.

Syntax and Execution Flow

The while loop header contains only a boolean condition. Before every iteration — including the very first — the condition is evaluated. If it is true, the body executes; if it is false, the loop exits and execution continues after the closing brace. Because the condition is checked before the first execution, a while loop may execute its body zero times. This is the key distinction between while and do-while: while is a pre-test loop, do-while is a post-test loop. The loop variable (or variables) that the condition depends on must be initialised before the loop, and must be updated inside the body, otherwise the condition never changes and the loop runs forever. Forgetting to update the loop variable is the most common source of infinite loops.
Java
// ── Basic while loop ─────────────────────────────────────────────────
int count = 0;

while (count < 5) {
    System.out.println("Count: " + count);
    count++;    // ← MUST update — otherwise infinite loop
}
// Output: 0  1  2  3  4

// ── Execution order ───────────────────────────────────────────────────
// 1. Evaluate condition (count < 5)
// 2. If true  → execute body, then back to step 1
// 3. If false → exit loop

// ── Condition false from the start — zero iterations ─────────────────
int n = 10;
while (n < 5) {
    System.out.println("Never printed");
}
// Body executes 0 times — this is valid, expected behaviour

// ── Variable must be declared OUTSIDE the loop ────────────────────────
// (unlike for loop which can declare in the header)
int i = 0;
while (i < 10) {
    System.out.print(i + " ");
    i += 2;   // step by 2
}
// Output: 0 2 4 6 8

// ── While loop equivalent to for loop ────────────────────────────────
// These two are exactly equivalent:

// for version:
for (int x = 0; x < 5; x++) {
    System.out.println(x);
}

// while version:
int x = 0;           // initialisation (before loop)
while (x < 5) {
    System.out.println(x);
    x++;             // update (inside body)
}

// Rule of thumb:
// Use for  → when iteration count is known before the loop
// Use while → when termination depends on a condition that changes
//             during execution

Condition-Controlled Patterns

The most important use of while loops is processing an unknown amount of data — reading lines from a file, consuming events from a queue, or processing digits of a number. The condition expresses what "there is more work to do" means for the specific problem. When the data runs out or the goal is achieved, the condition becomes false and the loop exits naturally. Another classic pattern is the sentinel-controlled loop, where a special sentinel value signals the end of input. The loop reads values and continues as long as the current value is not the sentinel. This pattern is common in user input processing and stream reading.
Java
// ── Pattern 1: Process digits of a number ────────────────────────────
int number = 12345;
int digitSum = 0;

while (number > 0) {
    digitSum += number % 10;   // extract last digit
    number   /= 10;            // remove last digit
}
System.out.println("Digit sum: " + digitSum);   // 15

// ── Pattern 2: Binary representation ─────────────────────────────────
int value = 42;
StringBuilder binary = new StringBuilder();

while (value > 0) {
    binary.insert(0, value % 2);   // prepend bit
    value /= 2;
}
System.out.println("Binary: " + binary);   // 101010

// ── Pattern 3: GCD (Euclidean algorithm) ─────────────────────────────
int a = 48, b = 18;

while (b != 0) {
    int temp = b;
    b = a % b;
    a = temp;
}
System.out.println("GCD: " + a);   // 6

// ── Pattern 4: Sentinel-controlled input ──────────────────────────────
Scanner scanner = new Scanner(System.in);
int total = 0;
int inputCount = 0;

System.out.println("Enter numbers (-1 to stop):");
int input = scanner.nextInt();

while (input != -1) {       // -1 is the sentinel
    total += input;
    inputCount++;
    input = scanner.nextInt();
}

System.out.println("Sum: " + total);
System.out.println("Count: " + inputCount);

// ── Pattern 5: Power of two ───────────────────────────────────────────
// Find the smallest power of 2 >= n
int n2 = 100;
int power = 1;

while (power < n2) {
    power *= 2;
}
System.out.println("Smallest power of 2 >= 100: " + power);   // 128

Reading from Files and Streams

While loops are the idiomatic way to consume data from readers, streams, and scanners because the end-of-data condition is naturally expressed as a boolean: "while there is more data, process it." The loop variable is the result of the read operation itself — null for BufferedReader.readLine(), -1 for InputStream.read(), and hasNextLine() as the condition for Scanner. The pattern is always the same: initialise the read variable before the loop, check it in the condition, use it in the body, and update it at the end of the body. Updating inside the body rather than in a header makes the intent clear even when the "update" is a complex read expression.
Java
// ── Reading all lines from a file ────────────────────────────────────
try (BufferedReader reader = new BufferedReader(
        new FileReader("data.txt"))) {

    String line = reader.readLine();   // initialise BEFORE loop

    while (line != null) {             // null signals end-of-file
        processLine(line);
        line = reader.readLine();      // update at END of body
    }
}

// ── More concise — assign in condition ───────────────────────────────
// Java allows assignment inside the condition expression
try (BufferedReader reader = new BufferedReader(
        new FileReader("data.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        processLine(line);
    }
}

// ── Reading raw bytes ─────────────────────────────────────────────────
try (InputStream in = new FileInputStream("file.bin")) {
    int byteValue;
    while ((byteValue = in.read()) != -1) {
        processByte((byte) byteValue);
    }
}

// ── Scanner — consuming all tokens ───────────────────────────────────
Scanner scanner = new Scanner(new File("numbers.txt"));
List<Integer> numbers = new ArrayList<>();

while (scanner.hasNextInt()) {
    numbers.add(scanner.nextInt());
}
scanner.close();

// ── Iterator pattern ─────────────────────────────────────────────────
// The enhanced for loop is backed by an Iterator
// Sometimes you need the Iterator directly (to call remove())
List<String> names = new ArrayList<>(
    List.of("Alice", "Bob", "Carol", "Dave"));
Iterator<String> it = names.iterator();

while (it.hasNext()) {
    String name = it.next();
    if (name.startsWith("C")) {
        it.remove();   // safe removal during iteration
    }
}
System.out.println(names);   // [Alice, Bob, Dave]

Infinite Loops and Controlled Exit

An infinite while loop — while (true) — intentionally has no exit condition in the header. Instead it relies on break, return, or throw inside the body to terminate. This pattern is clear and honest: the loop runs forever by design, and the exit conditions are explicit inside the body. It is commonly used for server main loops, event dispatch loops, game loops, and retry loops where the continuation condition is complex or emerges mid-body. Using while (true) with break is often cleaner than contorting a condition to fit the header, especially when the decision to exit occurs in the middle of the body after some computation has already happened.
Java
// ── Retry with exponential backoff ──────────────────────────────────
int     maxAttempts = 5;
int     attempt     = 0;
boolean success     = false;

while (true) {
    attempt++;
    try {
        callExternalService();
        success = true;
        break;                  // exit on success
    } catch (ServiceUnavailableException ex) {
        if (attempt >= maxAttempts) {
            throw new RuntimeException(
                "Service unavailable after " + attempt +
                " attempts", ex);
        }
        long delay = (long) Math.pow(2, attempt) * 100;
        System.out.println("Retry " + attempt +
            " in " + delay + "ms");
        Thread.sleep(delay);
    }
}

// ── Input validation loop ─────────────────────────────────────────────
Scanner scanner = new Scanner(System.in);
int validAge;

while (true) {
    System.out.print("Enter your age (1-120): ");
    if (!scanner.hasNextInt()) {
        System.out.println("Please enter a number.");
        scanner.next();   // consume invalid token
        continue;
    }
    validAge = scanner.nextInt();
    if (validAge >= 1 && validAge <= 120) {
        break;            // valid input — exit loop
    }
    System.out.println("Age must be between 1 and 120.");
}
System.out.println("Valid age: " + validAge);

// ── Game loop ─────────────────────────────────────────────────────────
GameState state = GameState.initial();

while (true) {
    state.render();
    Input input = state.readInput();

    if (input == Input.QUIT) break;

    state = state.update(input);

    if (state.isGameOver()) {
        state.showGameOverScreen();
        break;
    }
}

// ── Thread-safe server loop ───────────────────────────────────────────
volatile boolean running = true;   // volatile ensures visibility

while (running) {
    Request request = server.accept();
    processAsync(request);
}
// Another thread sets running = false to shut down

Common Mistakes and How to Avoid Them

The while loop's simplicity conceals several traps. The most dangerous is an infinite loop caused by forgetting to update the loop variable. The subtlest is the off-by-one error — deciding whether the condition should be < versus <= often requires careful thought. Using the wrong equality operator (= instead of ==) can cause a compile error in Java (unlike C), which actually protects developers from this common bug.
Java
// ── Mistake 1: Forgetting to update the loop variable ────────────────
int i = 0;
while (i < 10) {
    System.out.println(i);
    // i++ missing → INFINITE LOOP
}

// Fix:
while (i < 10) {
    System.out.println(i);
    i++;   // ← must update
}

// ── Mistake 2: Update in wrong place ─────────────────────────────────
int count2 = 1;
while (count2 <= 5) {
    count2++;             // update BEFORE use — skips first value
    System.out.println(count2);   // prints 2 3 4 5 6 (wrong)
}

// Fix: update AFTER use
int count3 = 1;
while (count3 <= 5) {
    System.out.println(count3);   // prints 1 2 3 4 5
    count3++;
}

// ── Mistake 3: Condition uses wrong operator ──────────────────────────
// In Java, assignment in condition causes compile error (not boolean)
// while (i = 10) { ... }  // ← compile error — int not boolean

// ── Mistake 4: Condition never becomes false ──────────────────────────
// Often from using wrong comparison operator
double x = 0.0;
while (x != 1.0) {        // floating-point arithmetic may skip 1.0 exactly
    x += 0.1;             // INFINITE LOOP — x may go 0.9999, 1.0000001
}

// Fix: use epsilon comparison for floating point
while (x < 1.0 - 1e-9) {
    x += 0.1;
}

// ── Mistake 5: Loop body is empty ────────────────────────────────────
// Semicolon after while creates empty body — easy to miss
while (count < 10);    // ← semicolon ends the statement
{                      // ← this block always runs once, not in loop
    System.out.println(count);
}

Related Topics in Control Statements