☕ Java

continue Statement

The continue statement skips the remainder of the current loop iteration and immediately jumps to the loop's update step (in a for loop) or condition check (in a while or do-while loop). Unlike break, which exits the loop entirely, continue only skips the current pass — the loop carries on with the next iteration. It is used to filter out unwanted values, skip error conditions, and avoid deeply nested if-else blocks inside loops.

How continue Works

When continue executes, the JVM immediately jumps to a specific part of the loop depending on the loop type. In a for loop it jumps to the update expression (the i++ part) and then re-evaluates the condition. In a while or do-while loop it jumps directly to the condition check. In all cases the remaining statements in the current iteration body are skipped, but the loop itself continues running. This makes continue conceptually the opposite of break: break exits the loop; continue skips to the next iteration. Both are selective — they only fire when a condition is true, allowing the loop to handle some values normally and skip or exit on others. A key insight is that continue is always replaceable by an if statement that wraps the skipped code. The choice between the two is a matter of style and readability. continue tends to produce flatter, less nested code; the if approach is sometimes more explicit about what is being skipped and why. Neither is always better — use whichever makes the intent clearer.
Java
// ── continue in a for loop — skip even numbers: ──────────────────────
for (int i = 0; i < 10; i++) {
    if (i % 2 == 0) {
        continue;           // skip the rest of the body for even i
    }
    System.out.print(i + " ");
}
System.out.println();
// Output: 1 3 5 7 9
//
// Execution trace for i=4 (even):
//   1. i=4, condition 4<10 true → enter body
//   2. 4%2==0truecontinue
//   3. Jump to update: i++ (i becomes 5)
//   4. Condition 5<10continue loop

// ── continue in a while loop: ─────────────────────────────────────────
int i = 0;
while (i < 10) {
    i++;                    // increment BEFORE continue to avoid infinite loop
    if (i % 3 == 0) {
        continue;           // skip multiples of 3
    }
    System.out.print(i + " ");
}
// Output: 1 2 4 5 7 8 10
// WARNING: if the increment was AFTER continue, i would stay at 3
// forever (infinite loop) — always ensure progress before continue
// in while loops.

// ── continue in a do-while loop: ─────────────────────────────────────
int j = 0;
do {
    j++;
    if (j == 5) continue;   // skip printing 5 but loop continues
    System.out.print(j + " ");
} while (j < 7);
// Output: 1 2 3 4 6 7

// ── Equivalent using if (no continue): ───────────────────────────────
// These two loops produce identical output:
for (int k = 0; k < 10; k++) {
    if (k % 2 != 0) {       // only print odd — skip even using if
        System.out.print(k + " ");
    }
}
// vs:
for (int k = 0; k < 10; k++) {
    if (k % 2 == 0) continue;  // skip even using continue
    System.out.print(k + " ");
}
// Both output: 1 3 5 7 9
// The continue version is preferred when the main logic
// is complex — it avoids nesting the main body inside an if.

continue for Input Filtering and Data Processing

continue shines in data processing loops where some records are invalid, irrelevant, or need to be skipped. Instead of wrapping the entire processing block in a large if condition — which adds a level of nesting — continue filters out unwanted records at the top of the loop body and keeps the main processing logic at the top level. This is the loop equivalent of the guard clause pattern used in methods with early return. The pattern is particularly common when reading data from files, processing collections, validating user input line by line, and filtering search results. Each filter condition becomes a single continue line at the top of the loop, clearly stating what is being excluded and why.
Java
// ── Data processing — skip invalid records: ─────────────────────────
String[] records = {"Alice,30", "Bob,", "Charlie,25", null, ",40", "Diana,28"};

System.out.println("Valid employees:");
for (String record : records) {
    // Skip null records:
    if (record == null) continue;

    // Skip blank records:
    if (record.isBlank()) continue;

    String[] parts = record.split(",");

    // Skip records with missing name:
    if (parts[0].isBlank()) continue;

    // Skip records with missing or invalid age:
    if (parts.length < 2 || parts[1].isBlank()) continue;

    int age;
    try {
        age = Integer.parseInt(parts[1].trim());
    } catch (NumberFormatException e) {
        continue;           // skip records with non-numeric age
    }

    if (age < 0 || age > 150) continue;  // skip unrealistic ages

    // Only valid records reach here:
    System.out.printf("  %-10s age=%d%n", parts[0].trim(), age);
}
// Output:
//   Alice      age=30
//   Charlie    age=25
//   Diana      age=28

// ── Compared to deeply nested if approach (harder to read): ──────────
for (String record : records) {
    if (record != null && !record.isBlank()) {
        String[] parts = record.split(",");
        if (!parts[0].isBlank() && parts.length >= 2
                && !parts[1].isBlank()) {
            try {
                int age = Integer.parseInt(parts[1].trim());
                if (age >= 0 && age <= 150) {
                    System.out.printf("  %-10s age=%d%n",
                        parts[0].trim(), age);
                }
            } catch (NumberFormatException e) {
                // swallow
            }
        }
    }
}
// Same output, but the logic is buried 4 levels deep.
// The continue version is significantly easier to read and maintain.

// ── Prime number sieve using continue: ───────────────────────────────
System.out.print("Primes up to 50: ");
OUTER:
for (int num = 2; num <= 50; num++) {
    for (int divisor = 2; divisor <= Math.sqrt(num); divisor++) {
        if (num % divisor == 0) {
            continue OUTER;     // num is not prime — skip to next num
        }
    }
    System.out.print(num + " ");
}
// Output: Primes up to 50: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47

Labelled continue — Skipping in Nested Loops

Just as labelled break exits a specific outer loop, labelled continue skips the current iteration of a specific outer loop. When the labelled continue fires, the remaining body of both the inner loop and the outer loop's current iteration is skipped, and control jumps to the outer loop's update step or condition check. Labelled continue is less commonly needed than labelled break, but it is genuinely useful when processing 2D data structures where a problem found in one row means the entire row should be skipped rather than just the current inner iteration. The label must be placed immediately before the loop it refers to, and the labelled continue must appear somewhere inside that loop (directly or in nested loops).
Java
// ── Labelled continue skips the outer loop's current iteration: ───────
ROW:
for (int row = 0; row < 4; row++) {
    for (int col = 0; col < 4; col++) {
        if (col == 2) {
            continue ROW;   // skip the rest of this row, go to next row
        }
        System.out.print("(" + row + "," + col + ") ");
    }
    System.out.print("[end of row] ");  // never reached — skipped by continue ROW
}
System.out.println();
// Output: (0,0) (0,1) (1,0) (1,1) (2,0) (2,1) (3,0) (3,1)
// Each row only prints col=0 and col=1 — col=2 triggers continue ROW
// which skips "[end of row]" and moves to row+1.

// ── Without label — only skips inner iteration: ───────────────────────
for (int row = 0; row < 4; row++) {
    for (int col = 0; col < 4; col++) {
        if (col == 2) {
            continue;       // only skips this col — inner loop continues
        }
        System.out.print("(" + row + "," + col + ") ");
    }
}
// Output: (0,0) (0,1) (0,3) (1,0) (1,1) (1,3) (2,0) ...
// col=2 is skipped but col=3 still executes.

// ── Practical: process rows of CSV, skip invalid rows entirely: ────────
String[][] table = {
    {"Alice",   "30",  "Engineer"},
    {"Bob",     "abc", "Manager"},   // invalid age — skip whole row
    {"Charlie", "25",  "Designer"},
    {null,      "28",  "Analyst"},   // null name — skip whole row
    {"Diana",   "32",  "Lead"},
};

System.out.printf("%-10s %-5s %-10s%n", "Name", "Age", "Role");
PROCESS_ROW:
for (String[] row : table) {
    // Validate all fields first — skip entire row on any error:
    for (String field : row) {
        if (field == null || field.isBlank()) {
            System.out.println("  [skipping row — null/blank field]");
            continue PROCESS_ROW;   // skip to next row
        }
    }
    int age;
    try {
        age = Integer.parseInt(row[1]);
    } catch (NumberFormatException e) {
        System.out.println("  [skipping row — invalid age: " + row[1] + "]");
        continue PROCESS_ROW;
    }
    System.out.printf("%-10s %-5d %-10s%n", row[0], age, row[2]);
}
// Output:
//   [skipping row — invalid age: abc]
//   [skipping row — null/blank field]
// Alice      30    Engineer
// Charlie    25    Designer
// Diana      32    Lead

continue vs break vs Restructured Code

Understanding when continue helps and when it hinders is a matter of experience and code review. continue reduces nesting and makes guard conditions explicit, which is its primary value. However, overusing continue in a loop with many scattered continue statements can make the flow hard to follow — the reader must track all the skip conditions to understand what the main body actually processes. The key principle is that each continue should express a single, clearly named exclusion condition. If the condition is complex, extract it into a method with a descriptive name. If there are more than three or four continue statements in a single loop, consider restructuring the loop or extracting the body into a method that uses early return instead.
Java
// ── Good use of continue — flat, readable guard conditions: ──────────
public void processOrders(List<Order> orders) {
    for (Order order : orders) {
        if (order == null)           continue;  // guard: skip null
        if (order.isCancelled())     continue;  // guard: skip cancelled
        if (order.getItems().isEmpty()) continue; // guard: skip empty

        // Main processing — only valid orders reach here:
        calculateTotal(order);
        applyDiscounts(order);
        sendConfirmation(order);
    }
}

// ── Each continue should have a clear, single reason: ────────────────
public void logErrors(List<LogEntry> entries) {
    for (LogEntry entry : entries) {
        if (entry.getLevel() != Level.ERROR) continue;  // only errors
        if (entry.isAcknowledged())          continue;  // only unacked
        if (entry.getTimestamp().isBefore(cutoff)) continue; // only recent

        sendAlert(entry);
    }
}

// ── Too many continues — consider restructuring: ─────────────────────
// BAD — too many scattered skips:
for (Product p : products) {
    if (p == null) continue;
    if (p.isDiscontinued()) continue;
    if (p.getStock() == 0) continue;
    if (p.getPrice() < 0) continue;
    if (!p.isApproved()) continue;
    if (p.getCategory() == null) continue;
    // actual processing...
}

// BETTER — extract filter to a method:
for (Product p : products) {
    if (!isEligibleForProcessing(p)) continue;
    // actual processing...
}

private boolean isEligibleForProcessing(Product p) {
    return p != null
        && !p.isDiscontinued()
        && p.getStock() > 0
        && p.getPrice() >= 0
        && p.isApproved()
        && p.getCategory() != null;
}

// ── continue vs break — choosing the right one: ───────────────────────
//
// Use CONTINUE when:
//   You want to skip the current iteration but keep looping
//   Filtering unwanted values from a data set
//   Skipping error cases while processing valid ones
//   Flattening nested if conditions inside a loop
//
// Use BREAK when:
//   You have found what you were looking for
//   The loop has no more useful work to do
//   An error condition means no further processing makes sense
//   You want to exit completely on a specific condition

Related Topics in Control Statements