skip to content

How should you correctly iterate over a multidimensional or jagged array in Java, and what bugs arise from doing it wrong?

level: middleimportance: should knowfreq 45%

answer

  1. inner bound = a[i].length, never a fixed width
  2. for-each is jagged-safe automatically
  3. new int[n][] rows can be null -> NPE
  4. a.length = rows; a[i].length = row width
  5. short row -> AIOOBE; long row -> silent skip

basics

~10 s

Loop the outer array with a.length, and for each row loop with that row's own a[i].length. Using a single fixed width or assuming rows are non-null causes out-of-bounds errors or NullPointerExceptions.

solid answer

~40 s

Because a 2D array is an array of arrays, correct iteration queries the length at each level: the outer loop runs to a.length (row count) and the inner loop runs to a[i].length (this row's length). With a jagged array you must use a[i].length per row, not a single column constant — assuming a fixed width throws ArrayIndexOutOfBoundsException on shorter rows and silently skips elements on longer ones. If the array was partially allocated (new int[n][]), some rows may be null, so dereferencing a[i].length or a[i][j] throws NullPointerException; guard with a null check or ensure full allocation. The enhanced for-loop (for (int[] row : a) for (int v : row)) handles ragged lengths automatically and is cleaner when you don't need indices, though it still NPEs on a null row.

code

java · 17 lines
java
int[][] a = { {1}, {2, 3}, {4, 5, 6} };  // jagged

// Correct: inner bound is a[i].length
long sum = 0;
for (int i = 0; i < a.length; i++)
    for (int j = 0; j < a[i].length; j++)
        sum += a[i][j];

// Cleanest when indices aren't needed (jagged-safe):
long sum2 = 0;
for (int[] row : a)
    for (int v : row)
        sum2 += v;

// WRONG: fixed width from row 0 -> AIOOBE / skipped elements
int cols = a[0].length;        // 1
// for (int j = 0; j < cols; j++) ... would miss most elements

go deeper

for a junior

Writes a correct nested loop using a.length and a[i].length and prefers for-each when indices aren't needed.

for a middle

Identifies the fixed-width and null-row bugs, knows which exception each produces, and chooses the safe iteration form.

for a senior

Reviews code for these pitfalls, enforces allocation/null contracts at boundaries, and reasons about silent-skip logic bugs versus thrown exceptions.

for a principal

Sets team conventions for safe array traversal, defensive boundaries around partially-allocated structures, and when to prefer collections or flattened layouts over raw nested arrays.

## The setup A Java multidimensional array is an **array of arrays**, so it has a length at *each* level: `a.length` (number of rows) and, for each row, `a[i].length` (that row's length). There is no global column count. Correct iteration must respect both levels independently — especially for **jagged** arrays where row lengths differ. ## The correct indexed pattern ```java for (int i = 0; i < a.length; i++) { // rows for (int j = 0; j < a[i].length; j++) { // THIS row's length process(a[i][j]); } } ``` The inner bound is `a[i].length`, re-evaluated for each row. This is the single most important rule. ## The enhanced for-loop (for-each) ```java for (int[] row : a) { // each row reference for (int v : row) { // each value in that row process(v); } } ``` This automatically uses each row's actual length, so it is naturally jagged-safe and avoids off-by-one bugs. Use it when you don't need the indices `i`/`j`. ## Bug 1: fixed-width inner loop ```java int cols = a[0].length; // assume all rows equal -- WRONG for jagged for (int i = 0; i < a.length; i++) for (int j = 0; j < cols; j++) // uses a[0]'s length for every row process(a[i][j]); ``` - If a later row is **shorter** than `a[0]`, `a[i][j]` throws **`ArrayIndexOutOfBoundsException`**. - If a later row is **longer**, the extra elements are **silently skipped** (a logic bug with no exception). ## Bug 2: null rows after partial allocation ```java int[][] a = new int[3][]; // rows are null! for (int[] row : a) for (int v : row) // NullPointerException on the first null row process(v); ``` `new int[n][]` allocates only the outer array; each `a[i]` is `null`. Both `a[i].length` and `a[i][j]` (and the for-each over a null `row`) throw **`NullPointerException`**. Either allocate every row before iterating, or guard: ```java for (int[] row : a) if (row != null) for (int v : row) process(v); ``` ## Bug 3: confusing the two lengths Using `a.length` where `a[i].length` is meant (or vice versa) gives wrong bounds: `a.length` is the row count, `a[i].length` is a row's element count. They are equal only for a square fully-allocated array, which masks the bug until someone passes a non-square input. ## Higher dimensions The rule generalizes: each nesting level needs its own length query (`a.length`, `a[i].length`, `a[i][j].length`, …), and any level may be null if partially allocated. ## Practical guidance - Prefer the **for-each** form when you don't need indices — it is jagged-safe by construction. - When you need indices, always bound the inner loop by `a[i].length`. - Treat `new int[n][]` outputs as possibly-null rows; document or enforce allocation at the boundary.

  • Why is the enhanced for-loop considered jagged-safe?
    Because each inner for-each iterates over the actual row object's elements, it always uses that row's real length — no fixed width to get wrong.
  • Does for-each protect you from null rows?
    No. If a row reference is null (e.g., after new int[n][]), the inner for-each over that null row throws NullPointerException; you still need a null guard or full allocation.

saying these in an interview costs you the question

  • Caching a[0].length and reusing it for every row
  • Assuming for-each protects against null rows (it still NPEs)
  • Mixing up a.length and a[i].length
  • Believing a non-square input can't occur

context