Java continue Statement with Examples and Pitfalls

Learn the Java continue statement in for, while and do-while loops, guard clauses, the while loop trap, switch, forEach lambdas and labeled continue.

Flow of a Java for loop where continue skips the rest of the body and jumps to the update

The continue statement in Java skips the rest of the current loop pass and starts the next one, so the loop itself keeps running. It works in for, for-each, while and do-while loops, and with a label it can start the next pass of an outer loop.

A continue lets a loop skip the elements it should not process, such as blank lines in a file, cancelled orders or invalid records, without wrapping the rest of the loop body in another if block.

The following example counts the data rows of a small CSV file and skips blank rows and comment rows.

String[] rows = {"apple,3", "", "# fruit stock", "kiwi,5"};
int imported = 0;
for (String row : rows) {
    if (row.isBlank() || row.startsWith("#")) {
        continue;                       // skip blank and comment rows
    }
    imported++;
}
int importedRows = imported;            // 2

Notice that the loop still visits all four rows, unlike a loop that ends with break. Further down, we look at where continue jumps in each loop type, guard clauses, a trap in while loops, nested loops and switch, and a CSV import that skips bad rows.

1. Where continue Jumps in Each Loop Type

A continue always ends the current pass of the innermost loop, but the next step depends on the loop type. The Java Language Specification defines it as completing the loop body early, so whatever normally follows the body runs next.

Flow of a Java for loop where continue skips the rest of the body and jumps to the update
In a for loop, continue skips the rest of the body but still runs the update and the condition check
LoopNext step after continue
forThe update expression (such as i++), followed by the condition check
for-eachThe next element, or the end of the loop when no element is left
whileThe condition check
do-whileThe condition check at the bottom of the loop

The following example adds up the order amounts of a day and skips refunds and zero amounts. In a for loop, the update i++ still runs after continue, so the loop moves on to the next amount.

int[] amounts = {40, -10, 25, 0, 15};
int total = 0;
for (int i = 0; i < amounts.length; i++) {
    if (amounts[i] <= 0) {
        continue;                       // i++ still runs
    }
    total += amounts[i];
}
int revenue = total;                    // 80

2. Skipping Unwanted Elements Early

A guard clause tests for the cases we want to skip at the top of the loop body and leaves the pass early. The main logic stays at one indentation level, instead of moving deeper with every extra if.

Say a billing job adds up the totals of the orders it should invoice. It skips cancelled orders and orders with a zero total. The following example uses a small Order record.

record Order(String status, double total) {}
static double billableNested(List<Order> orders) {
    double sum = 0;
    for (Order order : orders) {
        if (!order.status().equals("cancelled")) {
            if (order.total() > 0) {
                sum += order.total();
            }
        }
    }
    return sum;
}
static double billable(List<Order> orders) {
    double sum = 0;
    for (Order order : orders) {
        if (order.status().equals("cancelled")) {
            continue;
        }
        if (order.total() <= 0) {
            continue;
        }
        sum += order.total();
    }
    return sum;
}
List<Order> orders = List.of(new Order("paid", 20.0), new Order("cancelled", 50.0),
        new Order("paid", 0.0), new Order("paid", 12.5));
double nested = billableNested(orders);  // 32.5
double guarded = billable(orders);       // 32.5

Both methods return the same total. The guard version reads as a list of rules (skip cancelled orders, skip empty orders), and a new rule is one more if with continue, not another level of nesting.

3. The continue Trap in while and do-while Loops

In a while or do-while loop, continue jumps straight to the condition, so an update at the end of the body is skipped. If that update is the only thing that moves the loop forward, the loop never ends. The method below compiles, and it hangs on the first refund.

static int revenueHangs(int[] amounts) {
    int total = 0;
    int i = 0;
    while (i < amounts.length) {
        if (amounts[i] <= 0) {
            continue;                   // i++ below is skipped, i never changes
        }
        total += amounts[i];
        i++;
    }
    return total;
}

The fix moves the update to the top of the body, before any continue, so every pass advances the index. A for loop or a for-each loop avoids the problem entirely, because its update runs after every pass.

int[] amounts = {40, -10, 25, 0, 15};
int total = 0;
int i = 0;
while (i < amounts.length) {
    int amount = amounts[i];
    i++;                                // update before any continue
    if (amount <= 0) {
        continue;
    }
    total += amount;
}
int revenue = total;                    // 80

We check for this trap whenever we add a continue to an existing while loop or do-while loop, because the update in those loops often sits at the end of the body.

4. continue in Nested Loops and switch

A plain continue in an inner loop skips only the rest of the inner pass. In the following example, an employee’s weekly timesheet contains an invalid entry of -1 hours, and the plain continue skips only that entry, so the row still counts with its other days.

int[][] hours = {{8, 7, 9}, {6, -1, 8}, {5, 5, 5}};
int counted = 0;
for (int[] week : hours) {
    for (int h : week) {
        if (h < 0) {
            continue;                   // skips only the -1 entry
        }
        counted += h;
    }
}
int totalHours = counted;               // 53

If a timesheet with an invalid entry must be ignored completely, we label the outer loop and write continue rows. That ends the inner loop and starts the next pass of the outer loop. The labeled break and continue tutorial explains the label rules.

int[][] hours = {{8, 7, 9}, {6, -1, 8}, {5, 5, 5}};
int counted = 0;
rows:
for (int[] week : hours) {
    int weekSum = 0;
    for (int h : week) {
        if (h < 0) {
            continue rows;              // skips the whole timesheet
        }
        weekSum += h;
    }
    counted += weekSum;
}
int totalHours = counted;               // 39

Inside a switch statement that sits in a loop, continue is allowed and applies to the loop, whereas break applies to the switch. The example skips noise events before the counter.

String[] events = {"click", "noise", "buy", "click"};
int tracked = 0;
for (String event : events) {
    switch (event) {
        case "noise":
            continue;                   // next event, tracked++ is skipped
        default:
            break;                      // leaves only the switch
    }
    tracked++;
}
int trackedEvents = tracked;            // 3

Outside a loop, the compiler rejects continue, even inside a switch. It also rejects continue inside a switch expression and inside a lambda body, because the target loop would be outside them.

// does not compile: continue outside of loop
int code = 2;
switch (code) {
    case 2:
        continue;
}

In the lambda passed to forEach(), a return statement ends the lambda call for the current element, which has the same effect as continue.

List<String> tags = List.of("java", "", "loops");
List<String> kept = new ArrayList<>();
tags.forEach(tag -> {
    if (tag.isEmpty()) {
        return;                         // works like continue
    }
    kept.add(tag);
});
String result = kept.toString();        // "[java, loops]"

5. Importing CSV Rows and Skipping Bad Ones

A shop imports its stock levels from a CSV file that people edit by hand. The file contains a header comment, blank lines, rows without a quantity and quantities typed as words. The import should load every good row, skip the bad ones and report how many it skipped, instead of failing on the first bad row.

static int importStock(List<String> rows, Map<String, Integer> stock) {
    int skipped = 0;
    for (String row : rows) {
        if (row.isBlank() || row.startsWith("#")) {
            continue;                   // not data, not counted
        }
        String[] parts = row.split(",");
        if (parts.length != 2) {
            skipped++;
            continue;
        }
        try {
            stock.put(parts[0].strip(), Integer.parseInt(parts[1].strip()));
        } catch (NumberFormatException e) {
            skipped++;
        }
    }
    return skipped;
}
List<String> rows = List.of("# name,qty", "apple, 3", "", "pear", "kiwi,five", "plum,7");
Map<String, Integer> stock = new LinkedHashMap<>();
int skipped = importStock(rows, stock);  // 2
String loaded = stock.toString();        // "{apple=3, plum=7}"

Each continue handles one reason to skip a row, and the parsing code at the end only sees rows with two fields. For simple filtering without counters, a stream with filter() says the same thing without any continue.

List<String> rows = List.of("# name,qty", "apple, 3", "", "plum,7");
long dataRows = rows.stream().filter(r -> !r.isBlank() && !r.startsWith("#")).count();   // 2

6. continue Statement FAQs

Questions about continue in search results and on Stack Overflow mostly compare it with break or ask where it is allowed.

6.1. What is the difference between break and continue in Java?

A break ends the whole loop, and the program continues after it. A continue ends only the current pass, and the loop goes on with the next element or the next condition check.

int[] values = {1, 2, 3, 4};
int sumBreak = 0;
for (int v : values) {
    if (v == 3) {
        break;
    }
    sumBreak += v;
}
int sumContinue = 0;
for (int v : values) {
    if (v == 3) {
        continue;
    }
    sumContinue += v;
}
int withBreak = sumBreak;               // 3, stops before 3
int withContinue = sumContinue;         // 7, skips only 3

6.2. Can we use continue inside forEach() in Java?

No. The continue statement needs an enclosing loop, and the code passed to forEach() is a lambda body, so it does not compile. A return in the lambda skips the rest of the work for the current element, as shown in section 4.

6.3. Can continue be used in a switch statement?

Only when the switch sits inside a loop. The continue applies to that loop, not to the switch. A continue in a switch without a surrounding loop gives the compile error continue outside of loop.

6.4. Does continue work in a do-while loop?

Yes. It jumps to the condition at the bottom of the loop, and the loop goes on if the condition is true. Any update placed after the continue in the body is skipped, as section 3 shows.

6.5. Should we avoid continue in Java loops?

No. A continue at the top of the body that skips unwanted elements makes the loop easier to read than nested if blocks. It hurts readability when it appears deep inside a long body, where a reader can miss it, so we keep the skip checks at the top.

7. Conclusion

The continue statement ends the current pass of a loop and starts the next one. In a for loop, the update still runs, whereas in while and do-while loops Java goes straight to the condition, so the update must come before any continue.

Used at the top of a loop body, continue works as a guard clause that skips blank rows, cancelled orders or invalid records. A label lets it restart an outer loop, a return replaces it inside a forEach() lambda, and a stream filter() replaces it when we only need the matching elements.

8. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.