Enhanced-for ("for-each") syntax
Student can produce an enhanced-for loop that visits every element of an array, choosing the correct element type for the loop variable, and identify the colon : as the keyword-equivalent that separates the variable from the array reference.
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, forif (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 onlyforloop; there is no C-style three-part form. - C++:
for (int v : xs): range-basedfor, added in C++11. Same surface; same intent. - JavaScript:
for (const v of xs): noteof, notin. (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
Student can choose between enhanced-for and traditional-for for a given task by checking the three-condition fit test (every element, no index needed, no mutation through the loop variable), and defend the choice in one sentence.
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 usesfor i, v in enumerate(xs):. The convention is "use the enhanced-for unless you need the index." - C++:
for (auto v : xs)(range-based) versusfor (size_t i = 0; i < xs.size(); ++i). Same fit test. - Rust:
for v in &xsversusfor (i, v) in xs.iter().enumerate(). Rust'siter().enumerate()is the canonical "I need both" form.
The enhanced-for variable is a copy
Student can predict, before running the code, that assigning to the enhanced-for loop variable does not change the array; and distinguish the primitive case (value copy, no write-back possible) from the object case (reference copy, where mutating the referenced object does change what the array's slot points at).
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 = 99has the same no-effect behavior:vis a local name;xsis a list of values that are still bound to their slots. Mutation requiresfor i in range(len(xs)): xs[i] = 99or 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 &xsis by-reference (immutable);for v in &mut xsis by-reference (mutable). Like C++, Rust makes the choice syntactically explicit.