☕ Java

Array Traversal

Array traversal is the process of visiting every element in an array to read, process, or transform it. Java provides multiple traversal mechanisms, each with different trade-offs in readability, capability, and performance. The classic indexed for loop gives full positional control. The enhanced for-each loop provides clean read-only forward iteration. Arrays.stream() enables functional pipeline processing. Understanding when to use each mechanism — and what each can and cannot do — is fundamental to writing idiomatic, efficient Java. This entry covers every traversal pattern, their capabilities and limitations, two-dimensional traversal, reverse traversal, partial traversal, and performance considerations.

Indexed for Loop — Full Positional Control

The indexed for loop is the most powerful array traversal mechanism because it exposes the index directly. The index enables writing to elements, comparing adjacent elements, iterating in non-standard order (reverse, step-by-step, from a specific start point), and simultaneously iterating two arrays with a single loop. These capabilities are unavailable in the enhanced for loop, making the indexed for loop the right choice whenever the position of an element matters. The canonical form iterates from 0 (inclusive) to length (exclusive): for (int i = 0; i < arr.length; i++). The off-by-one variants are the most common source of array bugs. Using <= arr.length as the condition accesses one index past the end and throws ArrayIndexOutOfBoundsException. Using < arr.length - 1 stops one element short, silently skipping the last element. Getting the boundary condition right requires understanding that array indices run from 0 to length-1 inclusive. For performance-sensitive code, caching the array length in a local variable before the loop was once considered an optimization because some older JVMs recomputed arr.length on every iteration. Modern JVMs optimize this automatically through loop invariant code motion, making the cache unnecessary in most cases. The conventional form arr.length in the condition is preferred for clarity.
Java
// ── Basic indexed traversal ──────────────────────────────────────────
int[] numbers = {10, 25, 8, 42, 15, 37, 6};

// Read every element
for (int i = 0; i < numbers.length; i++) {
    System.out.print(numbers[i] + " ");
}
// 10 25 8 42 15 37 6

// ── Write to elements — only indexed loop can do this ─────────────────
for (int i = 0; i < numbers.length; i++) {
    numbers[i] *= 2;   // doubles every element in place
}
// numbers is now [20, 50, 16, 84, 30, 74, 12]

// ── Using the index for position-aware logic ──────────────────────────
String[] days = {"Mon","Tue","Wed","Thu","Fri","Sat","Sun"};
for (int i = 0; i < days.length; i++) {
    System.out.printf("[%d] %s%n", i, days[i]);
}

// ── Comparing adjacent elements — requires index ──────────────────────
int[] data = {3, 7, 2, 9, 4, 6};
int inversions = 0;
for (int i = 0; i < data.length - 1; i++) {   // length - 1: compare [i] with [i+1]
    if (data[i] > data[i + 1]) inversions++;
}
System.out.println("Inversions: " + inversions);

// ── Simultaneous traversal of two arrays ──────────────────────────────
String[] keys   = {"name", "email",              "role"};
String[] values = {"Alice", "alice@example.com", "admin"};

for (int i = 0; i < keys.length; i++) {
    System.out.printf("%-10s = %s%n", keys[i], values[i]);
}

// ── Off-by-one comparison ─────────────────────────────────────────────
int n = numbers.length;

for (int i = 0; i < n;     i++) { /* visits indices 0..n-1  — correct */ }
for (int i = 0; i <= n-1;  i++) { /* visits indices 0..n-1  — equivalent */ }
for (int i = 0; i <= n;    i++) { /* ArrayIndexOutOfBoundsException at i=n */ }
for (int i = 0; i < n - 1; i++) { /* visits indices 0..n-2  — misses last */ }

Enhanced for Loop — Clean Read-Only Iteration

The enhanced for loop (for-each) provides the cleanest syntax for forward iteration when the index is not needed. It eliminates the loop variable, the length comparison, and the increment — three sources of potential off-by-one errors. The intent "do this for each element" is expressed directly without any iteration plumbing. The enhanced for loop has an important limitation: the loop variable is a copy of the element's value (for primitives) or a copy of the reference (for objects). Assigning to the loop variable has no effect on the original array. For primitives, this means in-place modification is impossible. For objects, this means reassigning the variable to a different object does not change the array, but calling methods on the variable that mutate the object does affect the array's elements because both the variable and the array element reference the same object. The compiler transforms the enhanced for loop over an array into a standard indexed for loop — the generated bytecode is identical to the manual form. There is no performance difference. The choice between indexed and enhanced for loops is purely about expressiveness: use enhanced when the index is not needed, use indexed when it is.
Java
// ── Enhanced for loop — read elements ────────────────────────────────
int[] scores = {85, 92, 78, 96, 88};

int sum = 0;
for (int score : scores) {
    sum += score;
}
System.out.println("Sum: " + sum);           // 439
System.out.println("Average: " + sum / 5.0); // 87.8

// ── Enhanced for with reference types ─────────────────────────────────
String[] names = {"Alice", "Bob", "Carol"};
for (String name : names) {
    System.out.println(name.toUpperCase());  // reads and processes
}

// ── Loop variable is a COPY — reassignment does not affect array ───────
int[] nums = {1, 2, 3, 4, 5};
for (int num : nums) {
    num = num * 2;   // modifies local copy only
}
System.out.println(Arrays.toString(nums));  // [1, 2, 3, 4, 5] — unchanged!

// Use indexed loop for in-place modification:
for (int i = 0; i < nums.length; i++) {
    nums[i] = nums[i] * 2;   // modifies actual array element
}
System.out.println(Arrays.toString(nums));  // [2, 4, 6, 8, 10]

// ── Reference types: variable copy, object mutation IS visible ─────────
StringBuilder[] builders = {
    new StringBuilder("hello"),
    new StringBuilder("world")
};

for (StringBuilder sb : builders) {
    sb.append("!");     // mutates the object — array is affected
    // sb = new StringBuilder("replaced");  // reassignment — no effect on array
}

System.out.println(builders[0]);  // "hello!" — mutation propagated
System.out.println(builders[1]);  // "world!" — mutation propagated

// ── Compiler transforms for-each to indexed loop ─────────────────────
// This:
for (int score : scores) sum += score;

// Compiles to exactly this:
for (int i = 0; i < scores.length; i++) sum += scores[i];

// Zero performance difference — choose based on readability

Stream-Based Traversal

The Arrays.stream() method converts an array into a stream, enabling functional-style operations: filter, map, reduce, collect, and more. Stream-based traversal expresses what to compute rather than how to iterate. A for loop says "iterate from 0 to n-1 and do something each time"; a stream says "give me all elements satisfying this condition, transformed this way, reduced to this result." Streams come with overhead that indexed loops do not: object allocation for the stream pipeline, boxing overhead when using IntStream operations on primitive arrays (partially avoided by specialised IntStream, LongStream, and DoubleStream), and function dispatch for each element. For tight inner loops over large primitive arrays where performance is critical, the indexed loop is faster. For most application-level code where clarity is more important than microseconds, streams produce more readable, less error-prone code. The specialised primitive streams (Arrays.stream(int[]) returns IntStream, not Stream<Integer>) avoid boxing overhead for common aggregate operations. IntStream.sum(), IntStream.average(), IntStream.min(), and IntStream.max() are efficient and avoid the Integer boxing that Stream<Integer> would require for each element.
Java
// ── Basic stream traversal ───────────────────────────────────────────
int[] numbers = {5, 2, 8, 1, 9, 3, 7, 4, 6};

// Sum — IntStream.sum() is efficient, no boxing
int total = Arrays.stream(numbers).sum();
System.out.println("Sum: " + total);         // 45

// Average — returns OptionalDouble
OptionalDouble avg = Arrays.stream(numbers).average();
avg.ifPresent(a -> System.out.printf("Average: %.2f%n", a));  // 5.00

// Min and max
int min = Arrays.stream(numbers).min().getAsInt();  // 1
int max = Arrays.stream(numbers).max().getAsInt();  // 9

// ── Filter and count ──────────────────────────────────────────────────
long evenCount = Arrays.stream(numbers)
    .filter(n -> n % 2 == 0)
    .count();
System.out.println("Even numbers: " + evenCount);  // 4

// ── Map to new array ──────────────────────────────────────────────────
int[] doubled = Arrays.stream(numbers)
    .map(n -> n * 2)
    .toArray();
System.out.println(Arrays.toString(doubled));  // [10, 4, 16, 2, 18, 6, 14, 8, 12]

// ── String array processing ───────────────────────────────────────────
String[] words = {"hello", "world", "java", "streams"};

String result = Arrays.stream(words)
    .filter(w -> w.length() > 4)
    .map(String::toUpperCase)
    .sorted()
    .collect(Collectors.joining(", "));
System.out.println(result);   // HELLO, STREAMS, WORLD

// ── Reduce ────────────────────────────────────────────────────────────
int product = Arrays.stream(new int[]{1, 2, 3, 4, 5})
    .reduce(1, (a, b) -> a * b);
System.out.println("Product: " + product);   // 120

// ── forEach for side effects ──────────────────────────────────────────
Arrays.stream(words).forEach(System.out::println);

// ── Parallel stream — for large arrays on multi-core ─────────────────
long parallelSum = Arrays.stream(numbers).parallel().sum();
// JVM splits array into chunks, sums each on separate threads

Specialised Traversal Patterns

Several common traversal patterns appear repeatedly in algorithms and data processing. Reverse traversal iterates from the last index toward the first — required for algorithms that process from the end, remove elements while iterating, or build strings by prepending. Partial traversal processes only a sub-range of the array. Stride traversal processes every nth element. Two-pointer traversal uses two indices that approach each other from opposite ends, foundational to many array algorithms. Two-dimensional array traversal requires nested loops, with the outer loop iterating rows and the inner loop iterating columns. The order matters for cache performance: iterating row-first (accessing elements left-to-right within each row) is cache-friendly because array rows are stored contiguously in memory. Column-first iteration (accessing elements top-to-bottom within each column) jumps between non-contiguous memory locations, causing more cache misses — significant for large matrices.
Java
// ── Reverse traversal ────────────────────────────────────────────────
int[] arr = {1, 2, 3, 4, 5};

for (int i = arr.length - 1; i >= 0; i--) {
    System.out.print(arr[i] + " ");
}
// 5 4 3 2 1

// ── Reverse to build a result ─────────────────────────────────────────
String reversed = "";
String word = "Java";
for (int i = word.length() - 1; i >= 0; i--) {
    reversed += word.charAt(i);
}
System.out.println(reversed);   // avaJ

// ── Partial traversal — sub-range ─────────────────────────────────────
int[] data = {10, 20, 30, 40, 50, 60, 70, 80};
int fromIndex = 2, toIndex = 6;   // process [2, 6) — indices 2,3,4,5

for (int i = fromIndex; i < toIndex; i++) {
    System.out.print(data[i] + " ");
}
// 30 40 50 60

// ── Stride traversal — every nth element ──────────────────────────────
for (int i = 0; i < arr.length; i += 2) {
    System.out.print(arr[i] + " ");  // every other element: 1 3 5
}

// ── Two-pointer technique — palindrome check ──────────────────────────
char[] chars = "racecar".toCharArray();
boolean isPalindrome = true;

for (int left = 0, right = chars.length - 1;
         left < right;
         left++, right--) {
    if (chars[left] != chars[right]) {
        isPalindrome = false;
        break;
    }
}
System.out.println("Palindrome: " + isPalindrome);   // true

// ── 2D array traversal — row-major (cache-friendly) ───────────────────
int[][] matrix = {{1,2,3},{4,5,6},{7,8,9}};

// Row-major: inner loop over columns — contiguous memory access
for (int row = 0; row < matrix.length; row++) {
    for (int col = 0; col < matrix[row].length; col++) {
        System.out.printf("%3d", matrix[row][col]);
    }
    System.out.println();
}

// Column-major: inner loop over rows — non-contiguous, cache-unfriendly
for (int col = 0; col < matrix[0].length; col++) {
    for (int row = 0; row < matrix.length; row++) {
        System.out.printf("%3d", matrix[row][col]);
    }
    System.out.println();
}

// ── Accumulate while traversing ───────────────────────────────────────
int[] values = {3, -1, 4, -1, 5, -9, 2, 6};
int   positiveSum = 0, negativeSum = 0;

for (int v : values) {
    if (v >= 0) positiveSum += v;
    else        negativeSum += v;
}
System.out.println("Positive sum: " + positiveSum);   // 20
System.out.println("Negative sum: " + negativeSum);   // -11

Related Topics in Arrays

Array Basics
An array in Java is a fixed-size, ordered collection of elements of the same type stored in a contiguous block of memory. Once created, the size of an array cannot change. Arrays are the simplest and most fundamental data structure in Java — they underlie many higher-level collections and provide direct, indexed access to elements in constant time O(1). Every array in Java is an object, inherits from java.lang.Object, and can hold either primitives or references.
One-Dimensional Array
A one-dimensional array is a linear sequence of elements of the same type, accessed by a single integer index. It is the simplest array form in Java and the foundation for understanding multidimensional arrays and collection classes. One-dimensional arrays are used to store and process lists of values — student grades, product prices, daily temperatures, character sequences — any time you need to work with a fixed-size ordered collection of homogeneous data.
Two-Dimensional Array
A two-dimensional array in Java is an array of arrays — each element of the outer array is itself a one-dimensional array. It is used to represent tabular data with rows and columns: matrices, game boards, spreadsheet grids, pixel buffers, and any data that is naturally organised in two dimensions. In Java, a 2D array is declared with two sets of brackets and accessed with two indices: array[row][column].
Multidimensional Array
A multidimensional array in Java is an array with more than two dimensions — an array of arrays of arrays, and so on. While two-dimensional arrays suffice for most programming tasks, three-dimensional arrays are used for spatial data (x, y, z coordinates of a 3D grid), volumetric data (layers, rows, columns), RGB pixel buffers, and scientific computations involving tensors. Java supports arbitrary dimensionality, though arrays beyond three dimensions are rare in practice.