Loops #

A loop is the mechanism for executing a block of code repeatedly while a certain condition holds. Java provides four loop constructs — for, while, do-while, and the enhanced for — each with a different usage context. Choosing the right construct isn’t just a matter of taste, it’s a matter of readability: for communicates “I know how many iterations there will be”, while communicates “I iterate until the condition changes”, and for-each communicates “I don’t care about the index, only the elements”. This article covers all the constructs along with break/continue patterns, labeled loops for nested loops, and anti-patterns that often cause bugs like ConcurrentModificationException.

The for Loop #

for is the choice when the number of iterations is already known — whether from a fixed count or an array/collection’s length. The three parts in the for header are: initialization, condition, and update.

// Basic structure
for (initialization; condition; update) {
    // code repeated while the condition is true
}

// Forward iteration
for (int i = 0; i < 5; i++) {
    System.out.println("i = " + i); // 0, 1, 2, 3, 4
}

// Backward iteration
for (int i = 10; i > 0; i--) {
    System.out.println(i); // 10, 9, ..., 1
}

// Step of two
for (int i = 0; i <= 20; i += 2) {
    System.out.println(i); // 0, 2, 4, ..., 20
}
flowchart TD
    A("[Start]") --> B["Initialization\nint i = 0"]
    B --> C{"Condition?\ni < 5"}
    C -- false --> G("[End]")
    C -- true --> D["Execute\nloop body"]
    D --> E["Update\ni++"]
    E --> C

for Loop Variations #

// Multiple control variables
for (int i = 0, j = 10; i < j; i++, j--) {
    System.out.println("i=" + i + " j=" + j);
}

// Empty condition = infinite loop
for (;;) {
    // keeps running until a break
    if (exitCondition) break;
}

// Iterating an array with an index
String[] fruits = {"apple", "banana", "orange"};
for (int i = 0; i < fruits.length; i++) {
    System.out.println(i + ": " + fruits[i]);
}

// Backward iteration over an array
for (int i = fruits.length - 1; i >= 0; i--) {
    System.out.println(fruits[i]);
}

The while Loop #

while is the choice when the number of iterations isn’t known in advance — the loop runs while the condition is still true. The condition is evaluated before each iteration, so the body may never execute if the condition is immediately false.

// Basic structure — the condition is checked first
while (condition) {
    // code executed while the condition is true
}

// Example: read input until valid
Scanner scanner = new Scanner(System.in);
int input = -1;
while (input < 1 || input > 10) {
    System.out.print("Enter a number 1-10: ");
    input = scanner.nextInt();
}

// Example: process data from a stream
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
String line;
while ((line = reader.readLine()) != null) {
    process(line);
}

// Example: convergence search algorithm
double estimate = 1.0;
double target   = 2.0;
while (Math.abs(estimate * estimate - target) > 1e-10) {
    estimate = (estimate + target / estimate) / 2.0;
}

Infinite loops happen when the condition never becomes false. Make sure something inside the loop eventually makes the condition false:

// ANTI-PATTERN: the condition is never false — loops forever
int i = 0;
while (i < 10) {
    System.out.println(i);
    // forgot i++!
}

// CORRECT
int i = 0;
while (i < 10) {
    System.out.println(i);
    i++; // the condition eventually becomes false
}

The do-while Loop #

do-while executes the body first before checking the condition. This guarantees the body executes at least once — useful for interactive menus, first-time input validation, and situations where an initial execution is always required.

// The condition is checked AFTER the body executes
do {
    // this code is guaranteed to execute at least once
} while (condition);

// Classic example: interactive menu
Scanner sc = new Scanner(System.in);
int choice;
do {
    System.out.println("1. View data");
    System.out.println("2. Add data");
    System.out.println("0. Exit");
    System.out.print("Choice: ");
    choice = sc.nextInt();
    processMenu(choice);
} while (choice != 0);

// Example: input validation — try first, validate later
String input;
do {
    System.out.print("Enter a name (min 3 characters): ");
    input = sc.next();
} while (input.length() < 3);
flowchart TD
    A("[Start]") --> B["Execute\nloop body"]
    B --> C{"Condition?\nchoice != 0"}
    C -- true --> B
    C -- false --> D("[End]")

while vs do-while #

Aspectwhiledo-while
When the condition is checkedBefore each iterationAfter each iteration
Minimum body executions0 (if the initial condition is false)1 (always)
Typical usageUnknown condition, possibly 0 iterationsInput/menus, initial condition always needs one execution

Enhanced for (for-each) #

The enhanced for or for-each is the cleanest way to iterate over all elements of an array or a collection implementing Iterable. No index needed, no get(i) — just the element name.

// Array
int[] numbers = {10, 20, 30, 40, 50};
for (int n : numbers) {
    System.out.println(n);
}

// List
List<String> names = List.of("Andi", "Budi", "Cici");
for (String s : names) {
    System.out.println(s.toUpperCase());
}

// Set — order is not guaranteed
Set<String> cities = Set.of("Jakarta", "Surabaya", "Bandung");
for (String c : cities) {
    System.out.println(c);
}

// Map — iterating entries
Map<String, Integer> scores = Map.of("Andi", 85, "Budi", 90);
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

Limitations of for-each #

List<String> list = new ArrayList<>(List.of("a", "b", "c", "d"));

// ANTI-PATTERN 1: can't modify the collection while iterating
for (String s : list) {
    if (s.equals("b")) {
        list.remove(s); // ✗ ConcurrentModificationException!
    }
}

// CORRECT: use Iterator.remove() to delete while iterating
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    if (it.next().equals("b")) {
        it.remove(); // ✓ safe
    }
}

// OR use removeIf() — the most concise way
list.removeIf(s -> s.equals("b")); // ✓

// ANTI-PATTERN 2: can't access the index in for-each
for (String s : list) {
    // no way to know s's index without a separate counter variable
}

// CORRECT: use a regular for loop when you need the index
for (int i = 0; i < list.size(); i++) {
    System.out.println(i + ": " + list.get(i));
}

break and continue #

break stops the loop entirely; continue jumps to the next iteration. Both work in every kind of loop.

// break — exit the loop
for (int i = 0; i < 10; i++) {
    if (i == 5) break;       // the loop stops when i = 5
    System.out.println(i);   // prints 0, 1, 2, 3, 4
}

// continue — skip this iteration, move to the next
for (int i = 0; i < 10; i++) {
    if (i % 2 == 0) continue; // skip even numbers
    System.out.println(i);    // prints 1, 3, 5, 7, 9
}

// break in while — the "loop-and-a-half" pattern
while (true) {
    String input = scanner.next();
    if (input.equals("quit")) break;
    process(input);
}

Labeled break and continue #

When there are nested loops, ordinary break and continue only affect the innermost loop. Use a label to control an outer loop:

// Without a label — break only exits the inner loop
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (j == 1) break; // only exits the j loop
    }
    System.out.println("i still runs: " + i);
}

// With a label — break exits the outer loop too
outerLoop:
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (i == 1 && j == 1) break outerLoop; // exits both loops
        System.out.println("i=" + i + " j=" + j);
    }
}

// Labeled continue — skip an iteration of the outer loop
outerLoop:
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (j == 1) continue outerLoop; // continue to the next i iteration
        System.out.println("i=" + i + " j=" + j);
    }
}
flowchart TD
    subgraph "Nested Loop with Label"
        A["Outer loop: i=0..2"] --> B["Inner loop: j=0..2"]
        B --> C{"Condition\nbreak/continue?"}
        C -- "break (no label)" --> D["Exit inner loop\nOuter loop continues"]
        C -- "break outerLoop" --> E["Exit BOTH loops"]
        C -- "continue (no label)" --> F["Next j iteration"]
        C -- "continue outerLoop" --> G["Next i iteration"]
        C -- No --> H["Execute body"]
    end

Intentional Infinite Loops #

Some programming patterns genuinely need a loop that runs forever — like a server waiting for connections, or a game loop. Use while (true) or for (;;) and control it with break:

// Server loop — runs until shutdown
while (true) {
    try {
        Socket client = serverSocket.accept();
        handleClient(client);
    } catch (SocketException e) {
        System.out.println("Server stopped");
        break;
    }
}

// Polling with a timeout
long deadline = System.currentTimeMillis() + 5000; // 5 seconds
while (true) {
    if (dataReady()) break;
    if (System.currentTimeMillis() > deadline) {
        throw new TimeoutException("Timed out waiting for data");
    }
    Thread.sleep(100); // wait 100ms before checking again
}

Choosing a Loop Construct #

SituationChoice
Number of iterations known (0 to N)for
Iterating an array/collection, no index neededfor-each
Need the index or backward iterationfor with an index
Iterate until a condition changes, possibly 0 timeswhile
Must execute at least once (menu, validation)do-while
Modifying a collection while iteratingIterator or removeIf()
Modern collection transformation/filteringStream API
flowchart TD
    A{What are you iterating?} --> B[Array or Collection]
    A --> C[Unknown condition]
    A --> D[Known fixed count]
    B --> E{"Need the index\nor modification?"}
    E -- No --> F["for-each\nfor String s : list"]
    E -- Yes --> G["regular for\nor Iterator"]
    C --> H{"Must run\nat least once?"}
    H -- Yes --> I["do-while"]
    H -- No --> J["while"]
    D --> K["for\nfor int i = 0; i < n; i++"]

    style F color:#fff,stroke:#16a34a,stroke-width:2px
    style I color:#fff,stroke:#3b82f6,stroke-width:2px
    style J color:#fff,stroke:#3b82f6,stroke-width:2px
    style K color:#fff,stroke:#16a34a,stroke-width:2px

Summary #

  • for for a known iteration count — the three-part header (init; condition; update) communicates the loop bounds explicitly; use it when the N-th iteration is known.
  • while for an unknown condition — the condition is checked before each iteration; if the condition is immediately false, the body never executes.
  • do-while for at least one execution — the condition is checked after the body; great for interactive menus and input validation that needs one attempt first.
  • for-each for iterating elements — the cleanest way for arrays and Iterable; can’t be used when you need the index, backward iteration, or collection modification during iteration.
  • ConcurrentModificationException — don’t remove or add elements directly from a collection inside for-each; use iterator.remove() or removeIf() instead.
  • Labeled break/continue for nested loops — without a label, break only exits the innermost loop; label the outer loop to control which loop is affected.
  • Intentional infinite loops use while (true) — this pattern is valid for server loops and polling; always include a clear break condition and a timeout mechanism.
  • Prefer for-each and the Stream API — for modern code, for-each is safer and more concise; for complex collection transformation/filtering, consider the Stream API (stream().filter().map().collect()).

← Previous: Conditional Selection   Next: Functions →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact