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 --> Cfor 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 conditionfalse:// 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 #
| Aspect | while | do-while |
|---|---|---|
| When the condition is checked | Before each iteration | After each iteration |
| Minimum body executions | 0 (if the initial condition is false) | 1 (always) |
| Typical usage | Unknown condition, possibly 0 iterations | Input/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"]
endIntentional 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 #
| Situation | Choice |
|---|---|
| Number of iterations known (0 to N) | for |
| Iterating an array/collection, no index needed | for-each |
| Need the index or backward iteration | for with an index |
| Iterate until a condition changes, possibly 0 times | while |
| Must execute at least once (menu, validation) | do-while |
| Modifying a collection while iterating | Iterator or removeIf() |
| Modern collection transformation/filtering | Stream 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:2pxSummary #
forfor 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.whilefor an unknown condition — the condition is checked before each iteration; if the condition is immediately false, the body never executes.do-whilefor at least one execution — the condition is checked after the body; great for interactive menus and input validation that needs one attempt first.for-eachfor iterating elements — the cleanest way for arrays andIterable; 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 insidefor-each; useiterator.remove()orremoveIf()instead.- Labeled break/continue for nested loops — without a label,
breakonly 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 clearbreakcondition and a timeout mechanism.- Prefer for-each and the Stream API — for modern code,
for-eachis safer and more concise; for complex collection transformation/filtering, consider the Stream API (stream().filter().map().collect()).