The Java Textbook

Part 4 · data-collections

2-D arrays

An array of arrays, not a rectangle — and the FRQ that appears on every exam depends on knowing the difference.

By the end of this chapter you can

  • Declare and traverse a 2-D array in row-major order
  • Use the correct length for rows and columns
  • Traverse by column when a problem requires it
  • Explain why Java has no true 2-D array

One of the four free-response questions is about 2-D arrays, every year. It is the most predictable mark on the paper, and it turns on one structural fact.

Java has no 2-D array

int[][] grid is an array whose elements are arrays. That is not a pedantic distinction — it is why grid.length and grid[0].length mean different things, and why rows can have different lengths.

What a 2-D array actually is
public class Main {
    public static void main(String[] args) {
        int[][] grid = {
            {1, 2, 3},
            {4, 5, 6}
        };

        System.out.println("rows:            " + grid.length);
        System.out.println("cols in row 0:   " + grid[0].length);
        System.out.println("grid[1][2]:      " + grid[1][2]);

        // Each row is a separate object.
        System.out.println("row 0 is an int[]: " + (grid[0] instanceof int[]));

        // And rows need not match in length — a "jagged" array.
        int[][] jagged = new int[3][];
        jagged[0] = new int[]{1};
        jagged[1] = new int[]{1, 2};
        jagged[2] = new int[]{1, 2, 3};

        for (int[] row : jagged) System.out.print(row.length + " ");
        System.out.println();
    }
}

The exam uses rectangular arrays almost exclusively, but the underlying model is what makes grid[0].length the right way to ask for a column count — there is no single “width” stored anywhere.

Row-major traversal

The standard nested loop, and the one an FRQ expects unless it says otherwise:

Row by row, left to right
public class Main {
    public static void main(String[] args) {
        int[][] grid = {
            {1, 2, 3},
            {4, 5, 6}
        };

        int sum = 0;
        StringBuilder order = new StringBuilder();

        for (int r = 0; r < grid.length; r++) {
            for (int c = 0; c < grid[r].length; c++) {
                order.append(grid[r][c]).append(" ");
                sum += grid[r][c];
            }
        }

        System.out.println("visit order: " + order);
        System.out.println("sum: " + sum);
    }
}

Note grid[r].length rather than grid[0].length in the inner loop. On a rectangular array they are the same; on a jagged one only the first is correct, and writing it that way costs nothing.

The enhanced form works too, when positions are not needed:

Enhanced for, nested
public class Main {
    public static void main(String[] args) {
        int[][] grid = {{1, 2, 3}, {4, 5, 6}};

        int max = grid[0][0];
        for (int[] row : grid) {          // each element is a row
            for (int value : row) {       // each element is an int
                if (value > max) max = value;
            }
        }
        System.out.println("max: " + max);
    }
}

The outer variable is int[], not int. Declaring it int is a compile error, and it is a common one on a written FRQ where no compiler is watching.

Column-major traversal

When a question asks about columns, the loops swap and the bounds come from different places:

Column by column, top to bottom
public class Main {
    public static void main(String[] args) {
        int[][] grid = {
            {1, 2, 3},
            {4, 5, 6}
        };

        for (int c = 0; c < grid[0].length; c++) {   // columns: width
            int colSum = 0;
            for (int r = 0; r < grid.length; r++) {  // rows: height
                colSum += grid[r][c];
            }
            System.out.println("column " + c + " sums to " + colSum);
        }
    }
}

The indexing is still grid[row][column]always row first. Only the loop order changed. Writing grid[c][r] is the classic error, and on a non-square grid it throws immediately.

A worked FRQ-style task

Does any row contain only even numbers?
public class Main {
    static boolean hasAllEvenRow(int[][] grid) {
        for (int r = 0; r < grid.length; r++) {
            boolean allEven = true;
            for (int c = 0; c < grid[r].length; c++) {
                if (grid[r][c] % 2 != 0) {
                    allEven = false;
                    break;            // this row is settled
                }
            }
            if (allEven) return true; // found one, done
        }
        return false;
    }

    public static void main(String[] args) {
        System.out.println(hasAllEvenRow(new int[][]{{1,2},{4,6}}));  // true
        System.out.println(hasAllEvenRow(new int[][]{{1,2},{4,7}}));  // false
        System.out.println(hasAllEvenRow(new int[][]{{}}));           // true — vacuous
    }
}

Two structural points that earn rubric marks:

  • The flag is reset inside the outer loop. Declaring allEven outside would let one bad row poison every later one.
  • The empty row returns true, because “all of nothing is even” is vacuously satisfied. That is what the code does, and it is worth noticing rather than discovering in testing.

Practice