To loop over an array in Java you have four practical tools: the enhanced for loop, the classic indexed for loop, Arrays.stream(...) with a lambda, and IntStream.range(...) when you want index numbers. Each one suits a different situation, and choosing the wrong one is what produces confusing bugs such as assigning to the loop variable and watching the array stay unchanged.
What are the main ways to loop over an array in Java?
Here is the whole menu at a glance. The differences are not about speed, they are about what information you have available inside the loop body and whether you intend to modify the array.
| Style | You get | Reach for it when |
|---|---|---|
Enhanced for | Each value in turn | You only need to read elements |
Indexed for | The index and the value | You need positions, or you must write to the array |
Arrays.stream(...).forEach | Each value, as a lambda | You want to chain further processing |
IntStream.range(...) | A generated index | You want indices without an array |
The enhanced loop and the indexed loop cover almost everything you will write in practice. The stream variants earn their place when the loop body is part of a larger chain of operations, and the streams module later in this tutorial goes into that in detail. For now it is enough to know they exist and what they give you.
When does the enhanced for loop fit best?
The enhanced for loop, sometimes called the for-each loop, exists for the case where you want every element and nothing more. It reads as English: for each value in this array, do this.
public class ForEachLoop {
public static void main(String[] args) {
int[] steps = { 4200, 7800, 0, 6100 };
for (int value : steps) {
System.out.println("steps: " + value);
}
String[] days = { "Mon", "Tue", "Wed" };
for (String day : days) {
System.out.print(day + " ");
}
System.out.println();
}
}steps: 4200
steps: 7800
steps: 0
steps: 6100
Mon Tue Wed Two things are happening inside that syntax. The first half before the colon declares a variable of the array's element type, and that variable is a fresh copy for each pass. The second half names the array to walk. The loop ends when the array is exhausted, which means you never write length and never risk an off-by-one.
It works for object arrays just as well as for primitives, which is why it is the natural choice when you are walking a list of names, file paths or records.
When do you need the index as well as the value?
Reach for the indexed for loop when the answer to a question depends on where something is, not just on what it is. Finding the position of the highest reading, counting how many times a value appears, or reporting a failure with its position all need the index.
public class IndexedLoop {
public static void main(String[] args) {
int[] temps = { 18, 24, 21, 27 };
int sum = 0;
int max = temps[0];
int maxAt = 0;
for (int i = 0; i < temps.length; i++) {
sum += temps[i];
if (temps[i] > max) {
max = temps[i];
maxAt = i;
}
}
double avg = (double) sum / temps.length;
System.out.println("sum = " + sum);
System.out.printf("avg = %.1f%n", avg);
System.out.println("max = " + max + " at index " + maxAt);
for (int i = temps.length - 1; i >= 0; i--) {
System.out.print(temps[i] + " ");
}
System.out.println();
}
}sum = 90
avg = 22.5
max = 27 at index 3
27 21 24 18 Three details in that loop matter. The bound is i < temps.length, never i <= temps.length. The average is cast to double before dividing, because integer division would throw away the fraction and give 22 instead of 22.5. And the counting starts at zero, so maxAt is a position in the array rather than a human-friendly position, which is worth remembering when you print it.
Running the loop backwards, as the last three lines do, is the standard way to walk an array in reverse. Start at length - 1 and stop at zero, otherwise you step off the front of the array.
Why does assigning to the loop variable not change the array?
This is the single most common surprise in a first week of Java. The enhanced loop copies each element into the loop variable, so writing to the variable writes to the copy. The array itself is untouched.
public class ForEachTrap {
public static void main(String[] args) {
int[] scores = { 10, 20, 30 };
for (int s : scores) {
s = 0;
}
System.out.println("after for-each: " + scores[0] + " " + scores[1] + " " + scores[2]);
for (int i = 0; i < scores.length; i++) {
scores[i] = 0;
}
System.out.println("after indexed: " + scores[0] + " " + scores[1] + " " + scores[2]);
}
}after for-each: 10 20 30
after indexed: 0 0 0The enhanced loop still works perfectly for arrays of objects, because here the copy is a reference to the same object. Calling item.setDone(true) on each element changes the objects the array points at. What it cannot do is swap the references themselves, and it cannot write a new value into a primitive slot.
So the rule is simple. If you want to replace an element, use the indexed form. If you want to change the object an element refers to, either form works.
How do you loop over an array with a stream?
Arrays have a stream method that hands you a stream of their values, and a stream has a forEach method that takes a lambda. The pair reads well and slots neatly into longer chains.
import java.util.Arrays;
import java.util.stream.IntStream;
public class StreamLoop {
public static void main(String[] args) {
int[] nums = { 3, 7, 11, 4 };
Arrays.stream(nums).forEach(n -> System.out.println("value " + n));
System.out.println("sum = " + Arrays.stream(nums).sum());
String[] names = { "ana", "bo", "cy" };
IntStream.range(0, names.length)
.mapToObj(i -> names[i].toUpperCase())
.forEach(System.out::println);
}
}value 3
value 7
value 11
value 4
sum = 25
ANA
BO
CYIntStream.range(0, names.length) produces the numbers 0, 1, 2 without touching the array at all. That is exactly what you want when the array is incidental and the indices are the point, such as numbering rows in a report.
Be aware of the trade-off. A stream is an object you build before you consume it, so a one-line loop written as a stream is longer than the same thing written as an enhanced for. Streams pay off when you chain several operations; for a single pass they are just more to read. The Arrays utility class lesson covers the other helpers that come from the same package.
How do you skip or stop an array scan early?
Scanning an array to find one thing is a common shape, and continue and break are the two keywords that shape it. continue abandons the current element and moves to the next; break leaves the loop entirely. Order your conditions so the most common exit comes first, since the first matching branch wins.
public class EarlyExit {
public static void main(String[] args) {
String[] seats = { "A1", "", "A3", "A4", "" };
int empty = 0;
for (String seat : seats) {
if (seat.isEmpty()) {
empty++;
continue;
}
System.out.println("occupied: " + seat);
}
System.out.println("empty seats: " + empty);
System.out.println("first free seat index: " + firstFreeSeat(seats));
}
static int firstFreeSeat(String[] seats) {
for (int i = 0; i < seats.length; i++) {
if (seats[i].isEmpty()) {
return i;
}
}
return -1;
}
}occupied: A1
occupied: A3
occupied: A4
empty seats: 2
first free seat index: 1The helper method is worth copying into your own code. It returns the index of the first match and returns immediately, so there is no flag variable and no leftover found boolean to keep in sync. Returning -1 when nothing matches gives the caller a single value to test. When you need to leave two nested loops at once, a label plus break label does the job, and the control-flow module covers that in more detail.
Quick reference: picking a loop
- Read-only pass over values: the enhanced
forloop. - You need the position of a value: the indexed
forloop withi < array.length. - You need to overwrite elements: the indexed form, because the enhanced form writes to a copy.
- The loop body is one step in a longer chain:
Arrays.stream(...)andforEach. - You only need a counter:
IntStream.range(0, n). - You need to stop early from nested loops: a label plus
break label. - You are looking for one match: return the index from a helper method rather than setting a flag.
- Never write
i <= array.length; the last valid index islength - 1.
Try it yourself: write a program named ReportCard that holds five subject marks in an int[] called marks and five subject names in a String[] called subjects. Use the indexed for loop to print every subject with its mark, and to work out the highest mark and which subject it belongs to. Then use the enhanced for loop to print only the subjects whose mark is at least 50, and confirm that both loops read the same array without changing it. As a final step, convert every mark below 40 to zero using the indexed loop, then print the array again to prove the change stuck.