Concepts / Arrays as Objects and References

Arrays as Objects and References

Java does not have true two-dimensional arrays. A two-dimensional array is an array whose elements are one-dimensional arrays.

  • Programming

The Grid Picture and Where It Breaks

Many programmers arrive at Java already picturing a two-dimensional array as a neat grid: rows going across, columns going 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 is a good mental model for everyday indexing. But it rests on an assumption that does not hold in Java. Java does not have true two-dimensional arrays. What it has instead is an array whose elements are one-dimensional arrays. This article examines the grid assumption directly, then replaces it with a more accurate model that explains what Java is doing when you write a grid-shaped array.

The difference matters less for the code you type than for the code that behaves unexpectedly. Most of the time, reading and writing a cell such as A[1][2] works exactly the way the grid suggests. The gap between the picture and the real structure shows up in a few specific situations, and those are the situations this article teaches you to predict.

Tracing the Outer Array and Its Rows

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 outer array does not contain the numbers directly. It contains three references, and each reference leads to its own row.

A outer array3 references, type int[][]A[0]int[] with 4 slotsA[1]int[] with 4 slotsA[2]int[] with 4 slotsreferencereferencereference
What does int[][] look like in memory, and what does the outer array actually contain? The outer array holds one reference per row, and each reference leads to a separate int[] row.

Notice what this picture 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. The table of contents tells you where each page is, but the pages are separate things.

Reading int[][] as an Array of Arrays

The type names 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.

Read each pair of brackets from the outside in. The rightmost pair describes what the outer array's elements are. In int[][], the outer array's elements are of type int[]. Each element is therefore a variable that refers to one row. Each row, in turn, is an array of int. So a two-dimensional array is an array whose elements are one-dimensional arrays, and nothing more mysterious is going on.

java
Output
7
Notes on the code
  • int[][] declares an array whose elements are int[] references
  • new int[3][4] creates the outer array of 3 references and three 4-slot rows
  • A[1][2] first selects row 1, then slot 2 within that row

Following A[1][2] Step by Step

The grid picture still describes indexing well, so it is worth seeing how the two bracket pairs resolve. The expression A[1][2] is not one lookup into a rectangle. It is two successive lookups. The first lookup uses the outer array and selects a row reference. The second lookup uses that row and selects an element inside it.

gridouter array of rowsgrid[1]row reference selectedgrid[1][2]element valueindex 1index 2
How does grid[1][2] resolve step by step: first which row reference is selected, then which element within that row? Each bracket pair is one lookup.

Because the first lookup gives you a whole row, that row is a value in its own right. You can use it as a row, not only as a path to a single cell. This is the fact that becomes important in the next section.

Passing a Row to a Method

The source gives one situation in which the row-by-row structure matters directly. Because each row is a value of type int[], a row can be handed to any subroutine that asks for an int[] parameter. The method does not receive the whole grid. It receives the row itself, which is the same row object the grid already refers to.

java
Output
9
Notes on the code
  • The parameter type is int[], which matches the type of grid[2]
  • grid[2] passes a reference to the existing row, not a copy of it
  • Writing row[1] changes the same slot that grid[2][1] reads
Before
int[][] grid = new int[3][4];
// grid[2][1] holds 0

Row 2 holds 0 0 0 0

After
static void setSecond(int[] row) { row[1] = 9; }
setSecond(grid[2]);
// grid[2][1] now holds 9

Row 2 holds 0 9 0 0

grid[2][1]0grid[2][1]9
When grid[2] is passed to a method that sets its index 1 slot to 9, the method receives the same row object the grid already holds, so the change is visible through grid.

Mistakes Beginners Make with Row References

  • Common mistake

    Picturing the rows as one contiguous rectangle and assuming the layout is a single block

    Why it fails

    Each row is an independent one-dimensional array that the outer array points to. The rows are not three stretches laid end to end in one region.

    Do this instead

    Think of the outer array as a table of contents holding references, and each row as its own array object.

  • Common mistake

    Assuming that passing grid[2] to a method gives the method a copy of the row

    What it looks like
    setSecond(grid[2]) followed by reading grid[2][1] returns 9, not 0.
    Why it fails

    A row is a value of type int[], so the method receives the same row object the grid refers to.

    Do this instead

    Expect changes made inside the method to be visible through the original grid.

  • Common mistake

    Assuming every row must have the same length because the array looks rectangular

    Why it fails

    The outer array only guarantees that it holds references to int[] values. Each row is a separate array that can have its own length.

    Do this instead

    Check the length of each row separately, such as grid[i].length, before relying on it in a loop.

  • Common mistake

    Treating A[1][2] as a single lookup rather than a row selection followed by an element selection

What do you think happens?

After int[][] t = new int[3][];, what happens if you immediately run System.out.println(t[1][0]);?

Reveal answer
Answer:

It throws a NullPointerException, because t[1] has not been assigned a row and is null.

new int[3][] creates the outer array with three reference slots and leaves the row length unspecified. Each row reference starts out with no row object behind it. Indexing into t[1] before assigning it a row fails, because there is no array to index.

Practice: Predict the Row Behavior

MEDIUM

You have int[][] grid = new int[3][4]; and a method static void fill(int[] row) that sets row[0] = 5. Without running the code, predict the value of grid[1][0] after you call fill(grid[1]). Then explain in one sentence why the answer depends on the row being a shared reference rather than a copy.

Hints
  • Ask what type the method's parameter has, and what type grid[1] has.
  • Ask whether the outer array holds the numbers directly or holds references to rows.
EASY

Sketch the outer array and its rows for int[][] B = new int[2][3]; using boxes and arrows, then mark which box you would change to alter the value that B[0][2] reads.

Hints
  • The arrow from the outer array to row 0 is the path B[0] follows.
  • The final slot inside that row is index 2.

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 row arrays, each of type int[].
  • The grid picture is a useful mental model for everyday indexing such as A[1][2], which is really a row selection followed by an element selection.
  • Because each row is a value of type int[], passing a row to a method hands that method the shared row object, so changes made inside the method are visible through the original grid.
  • Rows are independent arrays, so their lengths and initialization are handled separately for each row.