6.2 Array Indexing and Traversal
Prerequisites: Array declaration, index boundaries, and loops
Estimated time: 20 minutes
You are ready to continue when: You can choose a loop for indices, element updates, or reverse access
scores contains five marks: 85, 92, 78, 95, and 88. Five handwritten print statements might be manageable, but a longer array needs a loop to handle its indices.
1. Index Loop Provides Position and Value
int[] scores = {85, 92, 78, 95, 88};
for (int index = 0; index < scores.length; index++) {
System.out.println("Student " + (index + 1)
+ ": " + scores[index]);
}Output:
Student 1: 85
Student 2: 92
Student 3: 78
Student 4: 95
Student 5: 88The loop condition uses index < scores.length. When the index equals the length, the loop ends, and no out-of-bounds access occurs. The length of an empty array is 0, so the condition fails on the first check, resulting in the loop body executing 0 times.
When modifying components in place, use the index:
for (int index = 0; index < scores.length; index++) {
scores[index] = Math.min(100, scores[index] + 5);
}This assumes the input scores have already been checked to lie in 0–100, then caps the adjusted result at 100. If the business allows values exceeding 100, this rule should be removed; the code should not secretly decide boundaries on behalf of the business.
2. Enhanced for When Only Values Matter
for (int score : scores) {
System.out.println("Score: " + score);
}The enhanced for sequentially retrieves the value of each component and assigns it to the loop variable score. It does not directly expose the index, making it suitable for reading, counting, and calling methods that only require the element value.
Reassigning the loop variable does not replace the array components:
int[] values = {1, 2, 3};
for (int value : values) {
value = 0;
}
System.out.println(java.util.Arrays.toString(values));
// [1, 2, 3]When referring to the loop variable as a copy, be mindful of the component type:
- For
int[], a copy of the integer value is made. - For arrays of reference types such as
String[]andPerson[], a copy of the reference value is made. Reassigning the loop variable does not replace the array components, but modifying the mutable object through the reference will still be observable in the array's references.
Java's parameter passing follows the same pattern: always pass by value. When an array is passed to a method, a copy of the array reference value is made. The method can modify the components through the copied reference, but it cannot replace the caller's variable simply by reassigning the parameter.
static void changeFirst(int[] values) {
values[0] = 99;
}
static void replaceLocally(int[] values) {
values = new int[]{7, 8, 9}; // Only modify the parameter variable
}Calling changeFirst(original) will modify original[0]; calling replaceLocally(original) will not make original point to a new array.
3. Reverse and Adjacent Access Require Prior Derivation of Boundaries
Reverse Output:
for (int index = scores.length - 1; index >= 0; index--) {
System.out.println(scores[index]);
}When the array is empty, the initial value is -1, and the condition fails immediately. The index here is int; if the language used were an unsigned index type, the reverse approach might require adjustment.
When comparing adjacent elements, the loop only needs to run up to the second-to-last position:
for (int index = 0; index + 1 < scores.length; index++) {
long difference = (long) scores[index + 1] - scores[index];
System.out.println(difference);
}Do not memorize the loop template first. First, write down which indices the loop will access, and then work backward to derive the conditions that ensure all accesses are valid.
4. Define Empty-Input Behavior for Statistics
Summing over an empty array can naturally return 0:
static long sum(int[] values) {
long total = 0L;
for (int value : values) {
total = Math.addExact(total, value);
}
return total;
}Calculating an average, however, requires deciding the meaning of an empty array:
static double average(int[] values) {
if (values.length == 0) {
throw new IllegalArgumentException("empty values");
}
return (double) sum(values) / values.length;
}If values is allowed to be null, the contract should specify whether it should be rejected, treated as an empty array, or have some other meaning. The above implementation causes null to produce NullPointerException, which suits an internal API that treats a null argument as a caller error. Public APIs often use Objects.requireNonNull(values, "values") to provide more explicit messages.
5. Choosing a Traversal Method
| Requirement | Recommended Approach | Reason |
|---|---|---|
| Read each value individually | Enhanced for | Avoid exposing unnecessary indices |
| Modify an array component | Index for | Writable values[index] |
| Need position or adjacent values | Index for | Position participates in calculation |
| Reverse access | Index for | Clearly control start and direction |
| Process multiple arrays at corresponding positions | Index for | First validate length relationships |
A stream API can also handle arrays, but before mastering loops, boundaries, and side effects, it's not advisable to wrap simple traversal in more complex expressions that are harder to debug.
Language Difference Window
Python's enumerate simultaneously provides both index and value:
scores = [85, 92, 78, 95, 88]
for index, score in enumerate(scores):
print(f"Student {index + 1}: {score}")C++17's range-based for can bind by value or by reference. for (int value : values) modifies local values, while for (int& value : values) can modify the original component. This differs from Java's enhanced for reference model, which cannot be simply analogized by appearance.
Common Issues
| Phenomenon | First Check |
|---|---|
| Out-of-bounds in the last iteration | Is it written as index <= values.length |
| Enhanced for loop modification is ineffective | Is the loop variable only being reassigned |
| Attempting to calculate average of empty array results in exception or NaN | Is an empty input strategy defined |
| Method modifies original array | Is a component written through a copied reference |
| Out-of-bounds access while traversing two arrays together | Is the length validated first or handled by the shorter length |
Before Moving On
Walk through the forward, reverse, and adjacent element loops with empty arrays, single-element arrays, and three-element arrays. When modifying components, indicate the position where the index is written back. When using the enhanced for, explain whether the loop variable copies a primitive value or a reference value. Calculating the average also requires deciding the agreement for empty input and null.