Room 5 of 14
· about 30 minutes
The enhanced for loop, and when it fits
The counted loop always works. This room is the shorter loop, and the three
conditions that have to hold before you may use it.
Tasks checked0 of 4
XP earned on this path0
Room complete
Notes
Enhanced-for ("for-each") syntax
The enhanced-for is a second loop form, designed for the read-every-element case. Its syntax is:
for (Type variable : collectionOrArray) {
// body uses `variable` for the current element
}
The colon (:) is read aloud as "in": "for each Type variable in collectionOrArray". The loop visits every element in order, binding the variable to a fresh value each iteration. There is no index variable, no condition, no update: those three concerns are gone.
final int[] xs = {10, 20, 30};
for (int v : xs) {
System.out.println(v); // prints 10, 20, 30
}
JLS §14.14.2 specifies the desugaring. The compiler rewrites the enhanced-for over an array into the equivalent traditional-for:
for (int #i = 0; #i < xs.length; #i++) {
int v = xs[#i];
System.out.println(v);
}
The hidden counter #i is created by the compiler and is not accessible in the body. That is what the enhanced-for is for: the value-at-each-slot case where the index is not needed.
When to use it
- Reading every element to compute a sum, average, max, or count.
- Printing every element.
- Checking whether any/every element satisfies a condition.
When not to use it
- The body needs the index: for a parallel-array access, for
[i] formatting, for if (i == 0) boundary handling. Those three conditions determine which loop to use. - The body needs to mutate the slot. The loop variable is a copy of the element; assigning to it does not change
xs. The rule that the loop variable is a copy covers this in detail. - The traversal must walk part of the array, walk in reverse, or skip elements.
In other languages
- Python:
for v in xs:: identical shape; the colon is part of the statement separator, not analogous to Java's. Python's enhanced-for is the only for loop; there is no C-style three-part form. - C++:
for (int v : xs): range-based for, added in C++11. Same surface; same intent. - JavaScript:
for (const v of xs): note of, not in. (for (k in obj) iterates keys, which is a separate construct.) - Go:
for _, v := range xs { … }: the blank identifier _ discards the index when not needed.
When the enhanced-for fits
The enhanced-for is the right loop exactly when the body needs the current element's value and nothing else. Three things must be true:
1. You visit every element. The enhanced-for has no start, end, or step controls: it walks the whole array in order, beginning to end. Partial traversals, reverse traversals, and stride-by-two traversals all need a traditional for. 2. You do not need the index. No i in the body, no [i-1] look-behind, no parallel-array access like prices[i] paired with names[i]. If you find yourself writing int i = 0; for (int v : xs) { … i++; } to manufacture an index, you wanted a traditional for from the start. 3. You do not modify the array through the loop. The loop variable is a copy of the value (or a copy of the reference, for object arrays); writing back through it does not change the array. The rule that the loop variable is a copy covers this.
If any of the three is false, the traditional for is the right tool. There is no rule that says "always prefer enhanced-for": readability is the goal, and a traditional for reads more cleanly when its features (index, partial bounds, custom step) are actually used.
A worked decision
// Q: sum all elements
int sum = 0;
for (int v : xs) { // enhanced-for: value-only, every slot
sum += v;
}
// Q: print "index 0: 10", "index 1: 20", ...
for (int i = 0; i < xs.length; i++) { // traditional: index needed for the label
System.out.println("index " + i + ": " + xs[i]);
}
// Q: copy xs into ys, doubled
for (int i = 0; i < xs.length; i++) { // traditional: writing to ys[i] needs an index
ys[i] = xs[i] * 2;
}
// Q: check if any element is negative
boolean anyNeg = false;
for (int v : xs) { // enhanced-for: value-only, no early break needed
if (v < 0) { anyNeg = true; break; }
}
The break in the last example is legal in either loop form. Both break and continue work inside the enhanced-for.
Why the rule is useful
Choosing the right loop form is a code-review signal. A reviewer reading for (int v : xs) knows immediately that the body does not need the index, does not need to mutate xs, and visits every element. That information shortcuts mental simulation of the loop. A traditional for carries the opposite signal: the reviewer should look for the reason the index is needed. Mixing the two forms inconsistently destroys the signal.
In other languages
- Python:
for v in xs: is the default; the indexed form uses for i, v in enumerate(xs):. The convention is "use the enhanced-for unless you need the index." - C++:
for (auto v : xs) (range-based) versus for (size_t i = 0; i < xs.size(); ++i). Same fit test. - Rust:
for v in &xs versus for (i, v) in xs.iter().enumerate(). Rust's iter().enumerate() is the canonical "I need both" form.
The enhanced-for variable is a copy
In an enhanced-for over an array, each iteration assigns the current element's value to the loop variable. For primitives, that is a copy of the value. For object types, it is a copy of the reference (the object itself is shared). In neither case does writing to the loop variable change the array slot.
final int[] xs = {1, 2, 3};
for (int v : xs) {
v = 99; // modifies the local variable v
}
System.out.println(xs[0]); // prints 1: xs is unchanged
The compiler-generated desugaring (JLS §14.14.2) makes this concrete:
for (int #i = 0; #i < xs.length; #i++) {
int v = xs[#i]; // copy of xs[#i] into v
v = 99; // reassigns v; no path back to xs[#i]
}
After the assignment int v = xs[#i], the variable v and the slot xs[#i] are independent storage. Changes to v do not propagate.
Object arrays: the reference is copied, the object is shared
For an array of objects, the copy is of the reference. The object on the heap is still the same one, and mutating it through the reference does change what the array's slot points to (because both v and the slot point at the same object). What does not work is reassigning the slot through v:
final Point[] points = { new Point(1, 1), new Point(2, 2) };
for (Point p : points) {
p.x = 99; // WORKS: mutates the Point object that both p and points[i] reference
}
// points[0].x == 99 now
for (Point p : points) {
p = new Point(0, 0); // reassigns the local p; does NOT change points[i]
}
// points[0] is still the same Point object as before
The distinction lines up with the rule that assignment copies a reference and not the object it points at. Java has no concept of an alias for an array slot; the for-each gives you the value (or the reference), not the slot itself.
How to write back to the array
Use the traditional for:
for (int i = 0; i < xs.length; i++) {
xs[i] = xs[i] * 2; // write through the slot
}
There is no shorter form for in-place mutation in Java. (Streams' Arrays.setAll exists but is out of scope for CSCD 210.)
In other languages
- Python:
for v in xs: v = 99 has the same no-effect behavior: v is a local name; xs is a list of values that are still bound to their slots. Mutation requires for i in range(len(xs)): xs[i] = 99 or a list comprehension. - C++: range-based
for (auto v : xs) copies; for (auto& v : xs) takes a reference and does allow write-back. C++ makes the choice explicit at the &; Java has no equivalent: the for-each is always by-copy. - Rust:
for v in &xs is by-reference (immutable); for v in &mut xs is by-reference (mutable). Like C++, Rust makes the choice syntactically explicit.