Java For Loop
The for loop repeats a block a controlled number of times. It gathers the three parts of a counting loop (initialization, condition, update) into one line, which makes it the standard choice for iterating over a range of numbers or over the indexes of an array. Java also has an enhanced for loop (for-each) for visiting every element of an array or collection.
Syntax
for (initialization; condition; update) {
// body
}
for (int i = 1; i <= 5; i++) {
System.out.println("Line " + i);
}
Output:
Line 1
Line 2
Line 3
Line 4
Line 5
Execution Order
The three parts do not all run at the same time. The exact order is:
1. initialization (once)
2. condition -> false? exit loop
3. body
4. update
5. go to step 2
For the example above:
| Step | i | Condition i <= 5 | Action |
|---|---|---|---|
| Init | 1 | ||
| 1 | 1 | true | print "Line 1", then i++ |
| 2 | 2 | true | print "Line 2", then i++ |
| ... | |||
| 5 | 5 | true | print "Line 5", then i++ |
| 6 | 6 | false | exit |
The update runs after the body, so the body sees i = 1 on the first pass, not 2. If the condition is false initially, the body never runs.
The Loop Variable's Scope
A variable declared in the initialization exists only inside the loop:
for (int i = 0; i < 3; i++) {
System.out.println(i);
}
System.out.println(i); // compile error: i cannot be resolved
This is usually what you want: the counter does not leak into the surrounding code, and the next loop can reuse the name i. If the final value is needed afterward, declare the variable before the loop:
int i;
for (i = 0; i < 10 && !found(i); i++) { }
System.out.println("Stopped at " + i);
Common Counting Patterns
// 0 to n-1: the standard form for array indexes
for (int i = 0; i < n; i++) { ... }
// 1 to n inclusive
for (int i = 1; i <= n; i++) { ... }
// counting down
for (int i = 10; i > 0; i--) { ... }
// stepping by more than one
for (int i = 0; i <= 100; i += 10) { ... } // 0, 10, 20, ..., 100
// powers of two
for (int p = 1; p <= 1024; p *= 2) { ... } // 1, 2, 4, ..., 1024
The choice of < versus <= decides whether the last value is included. i < n starting from 0 runs exactly n times, which matches array indexes 0 through n - 1. Using <= with an array length runs one time too many and throws ArrayIndexOutOfBoundsException on the last pass.
Example: sum and factorial
int n = 5;
int sum = 0;
long factorial = 1;
for (int i = 1; i <= n; i++) {
sum += i;
factorial *= i;
}
System.out.println("Sum: " + sum); // 15
System.out.println("Factorial: " + factorial); // 120
The accumulator variables are declared before the loop so they keep their values across iterations and remain available afterward. factorial is a long because factorials exceed int range quickly (13! is already larger than Integer.MAX_VALUE).
Iterating Over Arrays by Index
String[] fruits = {"apple", "banana", "cherry"};
for (int i = 0; i < fruits.length; i++) {
System.out.println(i + ": " + fruits[i]);
}
Output:
0: apple
1: banana
2: cherry
Using fruits.length rather than the literal 3 keeps the loop correct when the array size changes. The index form is needed when you need the position, want to modify elements in place, or need to look at neighboring elements:
int[] prices = {100, 250, 80};
for (int i = 0; i < prices.length; i++) {
prices[i] = prices[i] * 110 / 100; // add 10% in place
}
int[] readings = {3, 7, 4, 9, 2};
for (int i = 1; i < readings.length; i++) {
int change = readings[i] - readings[i - 1];
System.out.println("Change: " + change);
}
The second loop starts at 1 so that readings[i - 1] is always valid.
The Enhanced for Loop (for-each)
When you need every element but not its index, the enhanced for loop is shorter and cannot go out of bounds:
for (Type element : arrayOrCollection) {
// use element
}
String[] fruits = {"apple", "banana", "cherry"};
for (String fruit : fruits) {
System.out.println(fruit);
}
List<Integer> scores = List.of(88, 92, 75);
int total = 0;
for (int score : scores) {
total += score;
}
System.out.println("Average: " + (double) total / scores.size());
Read the header as "for each fruit in fruits". It works on arrays and on any class that implements Iterable (List, Set, Queue, and others).
Limits of for-each
The loop variable is a copy of each element, so assigning to it does not change the array:
int[] nums = {1, 2, 3};
for (int n : nums) {
n = n * 2; // changes the local copy only
}
System.out.println(Arrays.toString(nums)); // [1, 2, 3]
For objects, the copy is a reference, so calling methods on it does affect the object (for (Account a : accounts) a.applyInterest(); works). Replacing the element itself still requires an index loop.
Removing elements from a collection while iterating it with for-each throws ConcurrentModificationException:
for (String name : names) {
if (name.isBlank()) names.remove(name); // throws
}
names.removeIf(String::isBlank); // correct approach
| Need | Use |
|---|---|
| Read every element | for-each |
| The index, or positions of neighbors | Index for |
| Modify elements of an array | Index for |
| Iterate backward or skip elements | Index for |
| Remove items from a collection | removeIf or an Iterator |
Multiple Variables and Omitted Parts
The initialization and update can contain several comma-separated expressions:
// reverse an array in place: two indexes moving toward each other
int[] a = {1, 2, 3, 4, 5};
for (int left = 0, right = a.length - 1; left < right; left++, right--) {
int tmp = a[left];
a[left] = a[right];
a[right] = tmp;
}
// a is now [5, 4, 3, 2, 1]
All variables declared in the initialization must share one type.
Any of the three parts may be omitted. With all three omitted, for (;;) is an infinite loop, equivalent to while (true). In practice, if the initialization or update is missing, a while loop usually reads better.
Nested Loops
A loop inside another loop runs completely for each iteration of the outer one:
for (int row = 1; row <= 3; row++) {
for (int col = 1; col <= 4; col++) {
System.out.printf("%4d", row * col);
}
System.out.println();
}
Output:
1 2 3 4
2 4 6 8
3 6 9 12
The inner loop runs 4 times for each of the 3 outer iterations: 12 total. printf without a newline keeps a row on one line; the println after the inner loop ends each row.
Nested loops multiply work. Two nested loops over n items perform n² iterations, which is fine for 100 items (10,000 steps) and slow for 100,000 items (10 billion). When processing large data, check whether a HashMap or HashSet lookup can replace an inner loop.
Inner loops can depend on the outer variable:
for (int i = 1; i <= 4; i++) {
for (int j = 1; j <= i; j++) {
System.out.print("*");
}
System.out.println();
}
Output:
*
**
***
****
Practical Example: Monthly Sales Report
public class SalesReport {
public static void main(String[] args) {
String[] regions = {"North", "South", "East", "West"};
int[][] sales = {
{120, 135, 150}, // North: Jan, Feb, Mar
{200, 180, 210},
{90, 110, 95},
{160, 170, 175}
};
int grandTotal = 0;
int bestIndex = 0;
int bestTotal = Integer.MIN_VALUE;
for (int r = 0; r < regions.length; r++) {
int regionTotal = 0;
for (int monthSales : sales[r]) {
regionTotal += monthSales;
}
System.out.printf("%-6s %5d%n", regions[r], regionTotal);
grandTotal += regionTotal;
if (regionTotal > bestTotal) {
bestTotal = regionTotal;
bestIndex = r;
}
}
System.out.printf("%-6s %5d%n", "Total", grandTotal);
System.out.println("Top region: " + regions[bestIndex]);
}
}
Output:
North 405
South 590
East 295
West 505
Total 1795
Top region: South
The outer loop uses an index because it needs r to look up both the region name and its row of sales. The inner loop uses for-each because only the values matter. regionTotal is declared inside the outer loop so it resets to 0 for each region, while grandTotal and the best-region tracking are declared outside so they accumulate across all regions. Starting bestTotal at Integer.MIN_VALUE guarantees the first region always replaces it, even if all sales were negative.