Java Loops Explained: For, While, Do-While and For-Each
Java has four loops: while checks first, do-while runs at least once, for counts, and for-each visits every element. Scope rules and off-by-one errors.
- Course: Java study plan
- Module: Control flow
- Kind: Lesson
- Reading time: 14 min
- Runtime: OpenJDK 18
What are the types of loops in Java?
Java has four loops. while checks its condition before each iteration and may run zero times; do-while checks after the body, so it runs at least once; for puts the initialiser, condition and update in one header for counted iteration; and the enhanced for, or for-each, visits every element of an array or Iterable without an index.
Lesson
Java has four loop forms and they are not interchangeable: each states something different about what is known before the loop starts. Choosing the right one is half of writing a clear loop; the other half is getting the boundaries right, which is where the off-by-one errors live. This lesson covers all four, the scope rules, and the boundary habits that eliminate the classic bugs.
while: repeat while a condition holds
int n = 27, steps = 0;
while (n != 1) {
n = (n % 2 == 0) ? n / 2 : 3 * n + 1;
steps++;
}
The condition is checked before each iteration, so the body may run zero times. Use while when you do not know how many iterations there will be — reading until end of input, converging on a value, draining a queue. Something in the body must eventually make the condition false, or you have an infinite loop; while (true) with a break inside is the accepted way to write "loop until something happens in the middle".
do … while: at least once
String answer;
do {
answer = prompt("Continue? (y/n)");
} while (!answer.equals("y") && !answer.equals("n"));
The condition is checked after the body, so the body runs at least once — right for "ask, then check, repeat if bad". Note the semicolon after the while. Rarely needed; when you reach for it, you usually mean "do the thing once, then while" and this is exactly that.
for: counted iteration
for (int i = 0; i < n; i++) {
…
}
Three parts in the header, all optional: initialiser (runs once), condition (checked before each iteration), update (runs after each iteration). for (;;) is an infinite loop. The loop variable is scoped to the loop — i does not exist after the closing brace, which is why you can write for (int i …) twice in a row.
The idiom for indices is 0 to < n, never <= n - 1 or 1 … n — matching Java's zero-based, half-open convention everywhere. Counting down: for (int i = n - 1; i >= 0; i--). Stepping: for (int i = 0; i < n; i += 2). Two variables: for (int i = 0, j = n - 1; i < j; i++, j--) — the comma is allowed only in the init and update parts.
A for header can express things a while needs three lines for, but the reverse is also true — a for whose body modifies i is a while in disguise and should be written as one.
Enhanced for (for-each)
for (String name : names) { // any Iterable, or an array
System.out.println(name);
}
for (int v : new int[] {3, 1, 2}) { sum += v; }
for (char c : s.toCharArray()) { … }
for (Map.Entry<String, Integer> e : map.entrySet()) { … }
Reads every element in order, with no index and no way to go wrong on bounds. Use it whenever you do not need the index. Limitations you must know:
- No index. If you need
i, use a classicfor(or keep a counter). - Assigning to the loop variable does not change the collection.
for (int v : arr) v = 0;leavesarruntouched —vis a copy of each element. - You cannot remove from a collection you are iterating —
list.remove(x)inside a for-each overlistthrowsConcurrentModificationException(usually on the next iteration). Use anIteratorwithit.remove(), orremoveIf(Module 14). - The loop variable is fresh each iteration; it can be declared
finaland captured by a lambda.
Boundaries: the off-by-one family
Almost every loop bug is a boundary:
for (int i = 0; i <= arr.length; i++) arr[i] // ArrayIndexOutOfBounds at i == length
for (int i = 1; i < arr.length; i++) arr[i] // skips element 0
for (int i = 0; i < s.length() - 1; i++) … // skips the last char — sometimes intended (pairs)
while (i < n) { … } vs while (i <= n) { … } // runs n vs n + 1 times
Habits that prevent them:
- Half-open ranges: start inclusive, end exclusive,
< end. The count isend - start. - Test the loop on the smallest inputs by hand:
n = 0,n = 1,n = 2. - When comparing neighbours (
arr[i]andarr[i + 1]), stop ati < length - 1; when the loop touchesarr[i - 1], start at1. - Never modify the loop variable inside a
forbody; if you must skip, usecontinue.
Loop variable scope and lifetime
for (int i = 0; i < 3; i++) { }
System.out.println(i); // compile error: i is out of scope
int i;
for (i = 0; i < 3; i++) { }
System.out.println(i); // 3 — declared outside, survives
Declare the variable in the header unless you need its final value afterwards (searching for an index, for instance). A variable declared inside the body is created fresh each iteration — a new int or a new reference — which matters for lambdas and for objects: List<int[]> rows filled with a new int[3] created inside the loop holds distinct arrays; created outside, every entry is the same array.
Nested loops
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
grid[r][c] = r * cols + c;
}
}
Two loops give rows × cols iterations — quadratic when both are n. Name the variables for what they index (r/c, i/j for generic pairs) and keep the inner body short. Leaving a nested loop early needs a labelled break (next lesson) or a flag.
Infinite loops and termination
while (true), for (;;) and a for whose update never reaches the condition are legitimate when a break or return inside ends them; a loop with no exit is a bug, and one with an exit that is never reached is a bug the tests may not catch. For double counters, the loop may never hit the exact stopping value — count with an int and compute the double (Module 2).
Performance notes
- Hoist invariants: computing
list.size()in the condition is fine (it is O(1)) but calling a method that scans the collection each iteration is not. for (int i = 0; i < arr.length; i++)over an array is as fast as it gets; the JIT removes bounds checks it can prove safe.- For-each over an
ArrayListuses anIterator— negligible cost. Over aLinkedList, for-each is O(n) while indexedget(i)is O(n²) — always for-each there.
Interview angle
- "Difference between
whileanddo-while?" Condition before vs after;doruns at least once. - "Can you modify a list inside a for-each?" Not structurally —
ConcurrentModificationException. Use an iterator'sremove,removeIf, or a classic index loop going backwards. - "What is the scope of a
forloop variable?" The loop; it is gone after the closing brace. - "How many times does
for (int i = 0; i <= n; i++)run?"n + 1.
Key takeaways
whilefor unknown counts (checked first),do-whilefor at-least-once,forfor counted loops, for-each for "every element".- Half-open ranges (
0to< n) and testingn = 0, 1, 2by hand kill off-by-one errors. - The
forvariable lives only in the loop; body variables are fresh each iteration. - For-each gives copies of elements and forbids structural modification.
Common questions
What is the difference between while and do-while in Java?
A while loop checks its condition before each iteration, so its body can run zero times. A do-while loop checks after the body, so the body always runs at least once, which suits ask-then-validate input loops. The do-while ends with a semicolon after the condition.
Why do I get ConcurrentModificationException in a for-each loop?
Adding to or removing from a collection while a for-each loop iterates over it invalidates the loop's iterator, which throws ConcurrentModificationException, usually on the next iteration. Remove through an explicit Iterator with it.remove(), use removeIf, or loop backwards by index.
Can I modify an array inside a for-each loop in Java?
Not through the loop variable: it is a copy of each element, so for (int v : arr) v = 0; leaves the array unchanged. To write elements, use an indexed for loop and assign arr[i]. For objects the copy is a reference, so calling a method on it does change the object.
How do I avoid off-by-one errors in loops?
Use half-open ranges, start inclusive and end exclusive, as in for (int i = 0; i < n; i++), so the count is end - start. Test the loop by hand with n equal to 0, 1 and 2, and never change the loop variable inside a for body. A condition of i <= n runs n + 1 times.
What is the scope of a for loop variable in Java?
A variable declared in the for header exists only inside the loop and is gone after the closing brace, which is why two loops in a row can both declare int i. Declare it before the loop when you need its final value afterwards.
Exercises
Collatz steps
Read an integer n, then n positive integers. For each starting value x, repeatedly apply: if x is even, x = x / 2; otherwise x = 3x + 1 — until x becomes 1. Print steps=<count> peak=<largest value seen, including the start>.
Use a while loop, and a long for x — intermediate values can exceed int.
Input: n, then n integers. Output: n lines.
Example: 27 → steps=111 peak=9232; 1 → steps=0 peak=1.
Multiplication grid
Read an integer n (1 ≤ n ≤ 12). Print the n × n multiplication table with every cell right-aligned in 4 characters (%4d, no trailing separator), then diagonal=<sum of i*i for i in 1..n> and total=<sum of every cell>. Nested for loops, one printf per cell, one println per row.
Example: 3 →
1 2 3
2 4 6
3 6 9
diagonal=14
total=36In this module: Control flow
← switch — statements, expressions and arrows · break, continue, labels and return →