Indexing Into Two-Dimensional Arrays
Java does not have true two-dimensional arrays. A two-dimensional array is an array whose elements are one-dimensional arrays.
The Grid Picture You Bring to Java
Many programmers arrive at Java already picturing a two-dimensional array as a neat grid: rows running across, columns running down, and every cell sitting inside one solid rectangle of storage. That picture is useful, and in many situations it will serve you well. It rests on an assumption, however, that does not hold in Java. This article examines that assumption directly and replaces it with a more accurate model of what Java does when you write a grid-shaped array.
What do you think happens?
Given int[][] A = new int[3][4];, what do you think A[1] holds: a single int, a row of four ints, or the whole grid?
Reveal answer
A row of four ints, which is itself an array (an int[] of length 4).
Java treats A as an array whose elements are one-dimensional int arrays. So A[1] is one of those elements, and it is a complete int[] with four slots. Keep this answer in mind, because the rest of the article depends on it.
Why Java Has No True Two-Dimensional Arrays
Java does not have true two-dimensional arrays. What Java provides instead is a two-dimensional array in the sense that a two-dimensional array is an array whose elements are one-dimensional arrays. Java has no single rectangular block of storage that is declared as a grid. It has an outer array, and each of its elements is a separate one-dimensional array.
The grid picture is still a useful mental model for everyday work. Reading and writing a cell such as A[1][2] behaves the way a grid suggests, and most code never needs more than that. The grid picture stops being accurate, though, when you ask where the rows physically live and how they are connected. The accurate answer is that the outer array holds references to rows, and each row is its own array.
Reading the Type int[][]
The type names themselves reveal the real structure. Java follows a simple rule: for any base type, you can form a new type by adding a pair of square brackets, and the result means array of that base type. Start with int[], which means array of int. Now treat int[] as the base type and add brackets again. You get int[][], which means array of int[], or equivalently array of arrays of int. The type name is a construction recipe, and it tells you exactly what the outer array holds.
The type int[][] means an array whose elements are of type int[]. Each element of the outer array is a variable of type int[] that refers to one row.
Tracing What the Outer Array Holds
Consider a 3-by-4 array named A. Because A is an array of int[], you can name its three rows as A[0], A[1], and A[2]. Each of these is a complete one-dimensional array of int, and each has four slots. The diagram below shows the structure: the outer array holds three references, and each reference leads to its own row.
Notice what the diagram does not show. It does not show the three rows sitting end to end in one unbroken stretch, and it does not show a single rectangular region. Each row is an independent one-dimensional array that the outer array points to. A useful way to hold this in mind is that the outer array is the table of contents and the rows are the pages.
Following grid[r][c] One Index at a Time
Now trace a single access. Each index in A[1][2] is applied in order, from left to right. The first index selects an element of the outer array, and that element is a row. The second index then selects one slot inside that row.
Reading and writing a cell through two indexes
A is a 3-by-4 int array. Set the cell in row 1, column 2 to 5, then read it back.
- 1Select the row
A[1] is the element at index 1 of the outer array. That element is an int[] reference to the second row.
- 2Select the cell
A[1][2] applies index 2 to that row, which selects the third slot of the second row.
- 3Assign and read
Assigning 5 changes the slot inside row 1. Reading A[1][2] afterward returns that 5.
A[1][2] holds 5, and the row it lives in is the row that A[1] refers to.
3
4
5- A.length counts the rows held by the outer array, which is 3.
- A[0].length goes through the first row reference and counts its slots, which is 4.
- A[1][2] = 5 uses both indexes: the first picks the row, the second picks the cell.
Passing a Row to a Method
The row-by-row structure matters in one particularly important situation. Because each row is a value of type int[], a single row can be handed to any subroutine that asks for an int[] parameter. The expression A[1] is an int[] value, so it can be passed directly as an argument.
static void fill(int[] row) {
row[0] = 7;
}
public static void main(String[] args) {
int[][] A = new int[3][4];
fill(A[1]);
System.out.println(A[1][0]);
}- 1The method fill takes one parameter of type int[], which is exactly the type of a row.
- 2In main, A is created as a 3-by-4 array, so A[1] is a row of four ints.
- 3The call
fill(A[1])passes that row. The parameter row now refers to the same array object that A[1] refers to. - 4Inside fill, row[0] = 7 writes into that shared row, not into a copy of it.
- 5Back in main, A[1][0] reads the same slot and gets 7.
7The diagram shows why the caller's row changes. The method does not receive a copy of the row. It receives the same row reference, so writes made through the parameter are visible through A[1] once the method returns. This is the practical consequence of the array-of-arrays structure: a row is a real array object that can be shared, not a detached piece of a rectangle.
Mistakes Beginners Make With Rows
Common mistake Treating A[1] as a single number
What it looks likeint x = A[1]; where A is int[][]Why it failsA[1] is an element of the outer array, and that element has type int[]. It is a whole row, not an int.
Do this insteadUse int[] row = A[1]; to hold the row, or A[1][2] to reach a single int.
Common mistake Assuming the rows form one solid block of storage
Why it failsEach row is a separate one-dimensional array that the outer array refers to. The grid picture describes how you index, not how the rows are arranged.
Do this insteadThink of the outer array as holding references to rows, and reason about rows as independent array values.
Common mistake Expecting a passed row to be copied
What it looks likefill(A[1]) followed by reading A[1][0] after fill changes row[0]
Why it failsThe method receives the same row reference, so changes made through the parameter appear in the caller's row.
Do this insteadIf you need the caller's row unchanged, make your own copy before modifying it.
Practice Predicting Row Effects
Given int[][] A = new int[3][4]; and a method static void zero(int[] row) { row[3] = 0; }, what does A[2][3] hold after the call zero(A[2]) runs when the array was initially filled with 9 in every cell? Explain your answer in terms of which row the parameter refers to.
Hints
- Ask what type A[2] has, and whether the method receives a copy or a reference.
- Find which slot row[3] refers to, and whether that slot belongs to A[2].
Write a method static void clearRow(int[] row) that sets every slot of the row to 0. Then call it on the second row of a 3-by-4 array and print the whole array to confirm that only that row changed.
Hints
- A loop over
row.lengthvisits every slot in the row. - Only the row you pass in should change; the other rows should keep their values.
Putting the Two Models Together
- 1Java does not have true two-dimensional arrays. A two-dimensional array is an array whose elements are one-dimensional arrays.
- 2The type int[][] means array of int[], so each element of the outer array is a variable of type int[] that refers to one row.
- 3Indexing as A[r][c] works the way a grid suggests: the first index selects a row, and the second selects a cell within it.
- 4Because each row is a value of type int[], a single row can be passed to any method that accepts an int[] parameter, and the method shares that row with the caller.
Key Takeaways
- Java does not have true two-dimensional arrays; a two-dimensional array is an array whose elements are one-dimensional arrays.
- The type int[][] means array of int[], so the outer array holds references to rows.
- In A[r][c], the first index selects a row and the second selects a cell inside that row.
- Because each row is an int[] value, passing a row to a method shares that row with the caller.