4.2 Array Indexing, Bounds Checking, and Iteration Mechanics
Key Takeaways
- Java arrays use zero-based indexing where valid indices range strictly from 0 to array.length - 1.
- The capacity of an array is accessed via the public final field length without parentheses; calling length() causes a compilation error because length is a field, not a method.
- Evaluating an index outside the range of 0 to length - 1 immediately causes the JVM to throw an unchecked ArrayIndexOutOfBoundsException at runtime.
- Traversing an array can be performed using traditional indexed for loops (which allow modifying elements and custom stepping) or enhanced for-each loops (which provide clean, read-only iteration).
- Directly printing an array variable via System.out.println(arr) outputs its internal type descriptor and identity hashcode; java.util.Arrays.toString(arr) must be used to display formatted element contents.
4.2 Array Indexing, Bounds Checking, and Iteration Mechanics
[!NOTE] Exam Focus: In the 1Z0-811 examination, questions evaluating array indexing and loop iteration are among the most prevalent. Candidates must master zero-based index calculations, memorize the distinction between the
.lengtharray property and the.length()string method, identify off-by-one boundary traps that triggerArrayIndexOutOfBoundsException, and analyze the structural trade-offs between traditional indexed loops and enhancedfor-eachloops.
Once an array is allocated on the heap, an application interacts with its elements through indexed read and write operations. Because Java enforces strict memory safety guarantees, understanding how array elements are addressed and how loop control structures traverse them is fundamental to writing defect-free Java programs.
1. Zero-Based Indexing & Random Access
Java arrays utilize zero-based indexing. In an array containing $N$ elements:
- The first element resides at index
0. - The second element resides at index
1. - The last element resides at index
N - 1(orarr.length - 1).
Elements are accessed and updated using square bracket subscript syntax (arr[index]):
int[] temperatures = new int[3]; // Allocates slots at index 0, 1, and 2
temperatures[0] = 72; // Assigns value to the first slot
temperatures[1] = 68; // Assigns value to the second slot
temperatures[2] = 75; // Assigns value to the third slot
System.out.println(temperatures[0]); // Reads 72
temperatures[1] += 5; // Updates index 1 from 68 to 73
Because an index represents a numeric offset from the array's base heap address, any integer expression can serve as the index, including variables, arithmetic expressions, or method invocations returning an integral value:
int k = 1;
System.out.println(temperatures[k + 1]); // Accesses index 2, prints 75
2. The .length Property: Field vs. Method Distinction
Every array object instantiated on the Java heap possesses a single, built-in, public final instance variable named length. It reports the total number of element slots allocated when the array was created:
String[] planets = new String[8];
System.out.println(planets.length); // Prints: 8
Notice that planets.length returns 8 even if none of the element slots have been explicitly assigned and all currently hold null. The length property reflects the allocated capacity of the array, not the number of non-null elements stored within it.
The "Length Triad" Exam Trap
Oracle certification examinations relentlessly test candidate vigilance regarding the precise syntax used to measure size across different Java types. You must commit these three distinct rules to memory:
| Java Structure / Class | Measurement Syntax | Mechanism Category | Trailing Parentheses? |
|---|---|---|---|
Arrays (int[], String[]) | arr.length | Public final instance field | NO parentheses |
Strings (java.lang.String) | str.length() | Public instance method | YES parentheses |
Collections (ArrayList, HashSet) | list.size() | Public interface method | YES parentheses |
[!WARNING] Common Compiler Errors:
- Calling
arr.length()on an array results in:cannot find symbol: method length().- Calling
str.lengthon a String results in:cannot find symbol: variable length.- Calling
arr.size()on an array results in:cannot find symbol: method size().
3. Boundary Safety & ArrayIndexOutOfBoundsException
In unsafe languages like C, accessing an index beyond an array's bounds results in reading arbitrary memory or corrupting neighboring data buffers (buffer overflows). Java guarantees platform memory integrity by enforcing automatic runtime boundary validation on every array access.
Whenever an index expression is evaluated, the JVM checks the index against the array's capacity:
If the index violates this inequality—either by being negative (index < 0) or by meeting or exceeding the array's capacity (index >= array.length)—the JVM halts normal execution and throws an unchecked java.lang.ArrayIndexOutOfBoundsException.
int[] scores = {90, 85, 95}; // length is 3; valid indices are 0, 1, 2
System.out.println(scores[3]); // Throws ArrayIndexOutOfBoundsException: 3
System.out.println(scores[-1]); // Throws ArrayIndexOutOfBoundsException: -1
ArrayIndexOutOfBoundsException is an unchecked runtime exception (a direct subclass of IndexOutOfBoundsException, which extends RuntimeException). Because it is unchecked, the Java compiler does not require methods to declare it in a throws clause or enclose array accesses within try-catch blocks.
4. Loop Traversal Patterns: Traditional for vs. Enhanced for-each
Traversing an array involves visiting every element in sequence. Java provides two primary loop constructs for array iteration:
Pattern 1: The Traditional Indexed for Loop
The standard for loop uses an explicit integer counter variable that starts at 0 and increments until it reaches arr.length - 1:
int[] numbers = {10, 20, 30, 40};
// Forward sequential traversal
for (int i = 0; i < numbers.length; i++) {
System.out.println("Index " + i + ": " + numbers[i]);
}
Advantages of the traditional loop:
- Index Access: The loop counter
iis directly available to track position or coordinate multiple arrays. - In-Place Modification: Elements within the array can be overwritten directly (
numbers[i] *= 2;). - Flexible Stepping: Allows traversing backwards (
i--), skipping elements (i += 2), or terminating early based on complex conditions.
The Classic Off-by-One Loop Trap
A classic 1Z0-811 exam question presents a loop condition using <= instead of <:
int[] data = {5, 10, 15}; // length is 3; valid indices: 0, 1, 2
for (int i = 0; i <= data.length; i++) { // TRAP: i will reach 3!
System.out.print(data[i] + " ");
}
// Output:
// 5 10 15 followed immediately by: Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
Notice that the code executes successfully for i = 0, i = 1, and i = 2, outputting 5 10 15 . The exception does not occur when entering the loop; it fires on the fourth iteration when i equals 3.
Pattern 2: The Enhanced for-each Loop
Introduced in Java 5, the enhanced for-each loop provides a clean, elegant syntax for sequential, read-only iteration without managing an index counter:
String[] fruits = {"Apple", "Banana", "Cherry"};
for (String fruit : fruits) {
System.out.println(fruit);
}
Syntax Rules:
- The left side of the colon declares the iteration variable (
String fruit). Its type must match the component type of the array. - The right side of the colon specifies the array (or
Iterablecollection) being traversed.
[!IMPORTANT] The Read-Only Mutation Limitation: In an enhanced
for-eachloop traversing an array of primitives, the loop variable receives a copy of each element's value. Modifying the loop variable does not modify the underlying element in the array:int[] factors = {1, 2, 3}; for (int f : factors) { f = f * 10; // Modifies only the local stack variable 'f'! } System.out.println(factors[0]); // Still prints: 1 (array is NOT modified!)To modify array elements in place, you must use a traditional indexed
forloop (factors[i] = factors[i] * 10;).
5. Array Printing & Identity Representation
Passing an array reference variable directly to System.out.println() invokes the default toString() implementation inherited from java.lang.Object:
int[] numbers = {1, 2, 3};
System.out.println(numbers); // Prints something like: [I@15db9742
String[] names = {"Alice", "Bob"};
System.out.println(names); // Prints something like: [Ljava.lang.String;@6d06d69c
Anatomy of the Default Output:
[: Indicates a single-dimensional array.I: Type descriptor for primitiveint([I=int[]).Ljava.lang.String;: Type descriptor for reference typeString.@: Separator character.15db9742: The object's unsigned hexadecimal identity hashcode.
Formatting Array Contents with java.util.Arrays.toString()
To print the actual contents of an array formatted as a readable comma-separated string enclosed in square brackets, use the static utility method Arrays.toString() from the java.util package:
import java.util.Arrays;
int[] numbers = {10, 20, 30};
System.out.println(Arrays.toString(numbers)); // Prints: [10, 20, 30]
Consider the following code snippet:
What is the result when this code is compiled and executed?int[] numbers = {10, 20, 30};
for (int i = 0; i <= numbers.length; i++) {
System.out.print(numbers[i] + " ");
}
Consider the following code snippet:
What is the output produced when this code executes?int[] values = {1, 2, 3};
for (int v : values) {
v = v * 10;
}
System.out.println(values[0] + ":" + values[1] + ":" + values[2]);
Which of the following code snippets compiles cleanly and correctly determines the character count of a String and the element count of an array?