Skip to content
HopperHopper

Course navigation

Unit 4 · Data CollectionsTopic 4.11

2D Array Creation and Access

  • About 45 minutes
  • 5 sections
  • Runnable Java examples
  • Practice questions

Before you start

About 45 minutes

By the end you will be able to

  1. Explain why Java stores a 2D array as an array of arrays, and picture one as a grid of rows and columns.
  2. Create a 2D array with an initializer list or with new, and say what every element holds right after new runs.
  3. Read and change one element with two indices, row first and column second.
  4. Get a whole row as a 1D array and explain why changing that row changes the 2D array.
  5. Find the number of rows and columns with length, name the valid indices, and predict when an index causes an ArrayIndexOutOfBoundsException.

What to remember from earlier

Section 1 of 5

4.11.1 A Grid Is an Array of Arrays

A teacher keeps quiz scores for three students, Ana, Ben, and Cy, on four quizzes each. On paper that is a small table: one line per student, one box per quiz. With the 1D arrays from Topic 4.3 you could make one array per student:

int[] anaScores = {90, 85, 77, 92};
int[] benScores = {68, 74, 81, 88};
int[] cyScores = {95, 91, 89, 100};

That works for three students, but a class of thirty would need thirty variables with thirty names, and there would be no single variable that holds “the whole class.” What the teacher wants is one variable for the whole table.

Here is the key observation. A 1D array can hold any type of element: int values, String references, and so on. An array is itself an object, so a 1D array can also hold references to other arrays. Put the three student arrays into one outer array, and that outer array holds the whole table. Java calls this a two-dimensional array, or 2D array: an array whose elements are themselves 1D arrays of the same type. That is why people describe a 2D array as an array of arrays.

You can picture it in two ways. The first is a grid. Each inner array is a row, one line across the grid. The boxes at the same position in every row line up into a column, one line going down. Rows and columns are both numbered from 0, just like 1D array indices.

Col 0 Col 1 Col 2 Col 3
Row 0 90 85 77 92
Row 1 68 74 81 88
Row 2 95 91 89 100

Row 0 is Ana’s scores, row 1 is Ben’s, and row 2 is Cy’s. Column 2 holds everyone’s score on the third quiz: 77, 81, and 89.

The second picture shows how Java actually stores it. The variable refers to one outer array with three elements, and each of those elements refers to a row array:

scores --> outer array
           index 0 --> [ 90 | 85 | 77 | 92  ]
           index 1 --> [ 68 | 74 | 81 | 88  ]
           index 2 --> [ 95 | 91 | 89 | 100 ]

The grid picture is the one you will use most when reading code. The array-of-arrays picture explains why the rules in the rest of this chapter work the way they do, so keep both in mind.

Key idea

A 2D array is an array whose elements are 1D arrays. Each inner array is one row of a grid, and the elements at the same position in every row form a column.

Declaring and filling a 2D array

The type of a 2D array has two pairs of square brackets. int[] means “array of int,” so int[][] means “array of int[],” an array of arrays of int. The fastest way to create a small 2D array is with an initializer list, just as in Topic 4.3. The difference is that each element of the outer list is itself an initializer list for one row.

Worked example: Building the quiz table

Read this declaration one brace at a time. It is spread over three lines only so the rows line up; Java would accept it on one line.

int[][] scores = { {90, 85, 77, 92},
                   {68, 74, 81, 88},
                   {95, 91, 89, 100} };
Piece What it means
int[][] The type: an array whose elements are int[] arrays
scores The variable, which will hold a reference to the outer array
outer { … } The outer array. It has three elements separated by commas, so 3 rows
{90, 85, 77, 92} The first element of the outer array: row 0, a 1D array of four int
{68, 74, 81, 88} Row 1
{95, 91, 89, 100} Row 2

Java counts the inner lists to get the number of rows (3) and counts the values inside a row to get the number of columns (4). The result is the 3-by-4 grid pictured above. People always say the number of rows first: “a 3-by-4 array” has 3 rows and 4 columns.

A 2D array can hold object references as well as primitives. Change one thing, the element type, and the same pattern builds a seating chart of names:

String[][] seats = { {"Ana", "Ben", "Cy"},
                     {"Dee", "Eli", "Fay"} };

The type is String[][], an array of String[] rows. There are two inner lists, so 2 rows, and each holds three names, so 3 columns. Row 1, column 0 is "Dee": count rows from 0 (row 0 is the first line, row 1 the second), then count across from 0. Each box holds a reference to a String object, exactly as the boxes of a String[] did in Topic 4.3.

Every row in these examples has the same number of elements, so the grid is a rectangle. That is always the case in this course. (Java can build a grid whose rows have different lengths, but such grids are outside AP CSA, and you will not see them on the exam.)

Watch out

Declaring a 1D type for 2D data. A student writes the quiz table with only one pair of brackets on the type:

WrongDimensions.java
public class WrongDimensions
{
    public static void main(String[] args)
    {
        int[] scores = { {90, 85, 77, 92}, {68, 74, 81, 88} };
        System.out.println(scores[0]);
    }
}
WrongDimensions.java:5: error: illegal initializer for int
        int[] scores = { {90, 85, 77, 92}, {68, 74, 81, 88} };
                         ^

int[] promises that each element of the outer list is an int. The first element is {90, 85, 77, 92}, which is a whole row, not an int, so Java refuses to compile and reports the problem at each inner list. The number of bracket pairs in the type must match the nesting of the braces: two levels of braces need int[][]. The fix is to write int[][] scores = …. (The wording of compiler messages can differ a little between Java versions.)

Pause and try

Question 1 · Multiple choice

Which declaration creates a 2D array with 2 rows and 3 columns?

  1. int[][] g = { {1, 2, 3}, {4, 5, 6} };
  2. int[][] g = { {1, 2}, {3, 4}, {5, 6} };
  3. int[] g = { {1, 2, 3}, {4, 5, 6} };
  4. int[][] g = {1, 2, 3, 4, 5, 6};
Check your reasoning

A. The number of inner lists is the number of rows, and the number of values in each inner list is the number of columns. Choice A has two inner lists of three values each: 2 rows, 3 columns.

  • B is wrong: it has three inner lists of two values, so it is 3 rows by 2 columns. This choice swaps rows and columns.
  • C is wrong: the type int[] has only one pair of brackets, so each element must be an int, and Java reports illegal initializer for int.
  • D is wrong: the type int[][] says each element is an int[] row, but the list contains plain numbers, so Java reports incompatible types: int cannot be converted to int[]. A 2D initializer list needs an inner list for every row.

Reread 4.11.1 A Grid Is an Array of Arrays if you chose differently.

Question 2 · Short answer

Using the picture of how Java stores scores (the outer array with arrows to three rows), explain in one or two sentences why a 2D array is called an “array of arrays.” How many elements does the outer array of scores have?

Check your reasoning

The outer array’s elements are not numbers. Each element is a reference to a 1D int array, one per row, so the 2D array is an array whose elements are arrays. The outer array of scores has 3 elements, one for each row. (The 12 numbers live inside the three row arrays.)

A complete answer says that each element of the outer array is itself an array (a row), and gives 3 as the number of outer elements, not 12.

Reread 4.11.1 A Grid Is an Array of Arrays if your answer differed.

Section 2 of 5

4.11.2 Reading and Changing One Element

The teacher now wants Ben’s score on the third quiz. In a 1D array, one index picks one box. In a grid, one number is not enough: you have to say which row and which column. Think of a seat in a theater, which needs both a row and a seat number. “Seat 5” alone could be any of a dozen seats.

Java uses two pairs of square brackets, one index in each. The first index is the row index and the second is the column index:

scores[row][col]

The order is always row first, column second. The array-of-arrays picture shows why this works with no new rules. scores[1] follows index 1 of the outer array to Ben’s row, which is an ordinary 1D array. Then [2] picks index 2 of that row, just like any 1D array access from Topic 4.3. So scores[1][2] is really two 1D accesses in a row, read left to right.

Key idea

The expression grid[r][c] names the element in row r, column c. The first index always picks the row and the second always picks the column, and both count from 0.

Worked example: Reading scores from the table

Each print statement uses two indices. Before reading the output, find each element in the grid in 4.11.1.

int[][] scores = { {90, 85, 77, 92},
                   {68, 74, 81, 88},
                   {95, 91, 89, 100} };
System.out.println(scores[1][2]);
System.out.println(scores[2][0]);
System.out.println(scores[0][3]);
Line Expression Row (first index) Column (second index) Value
4 scores[1][2] 1, Ben’s row 2, the third quiz 81
5 scores[2][0] 2, Cy’s row 0, the first quiz 95
6 scores[0][3] 0, Ana’s row 3, the fourth quiz 92
81
95
92

Notice that the column index on line 4 is 2, but it names the third quiz. Counting from 0 works in both directions, exactly as with 1D arrays.

scores[r][c] is a variable, not just a value. Anywhere you could put a plain int variable, you can put scores[r][c]: on the left of = to store a new value, with compound assignment such as += (Topic 1.6), or on the right side of an expression. This next example changes three elements.

int[][] scores = { {90, 85, 77, 92},
                   {68, 74, 81, 88},
                   {95, 91, 89, 100} };
scores[0][2] = 80;
scores[1][1] += 6;
scores[2][3] = scores[2][3] - scores[0][2];
System.out.println(scores[0][2]);
System.out.println(scores[1][1]);
System.out.println(scores[2][3]);
80
80
20
  • Line 4 stores 80 in row 0, column 2. Ana’s 77 is gone, replaced the same way any assignment replaces a value.
  • Line 5 means scores[1][1] = scores[1][1] + 6. Ben’s 74 becomes 80.
  • Line 6 evaluates the right side first: scores[2][3] is 100 and scores[0][2] is now 80 (line 4 already changed it), so the result is 20, stored back in row 2, column 3.

Only the three named boxes change. Every other element keeps its value.

Watch out

Swapping the row and column. A student wants Cy’s score on quiz 1 (column 1) and writes:

int[][] scores = { {90, 85, 77, 92},
                   {68, 74, 81, 88},
                   {95, 91, 89, 100} };
System.out.println("Cy, quiz 1: " + scores[1][2]);
Cy, quiz 1: 81

Cy’s row is row 2, so the correct value is 91. The student wrote the indices in the order of the sentence, “quiz 1 for Cy,” instead of the order Java uses. scores[1][2] is row 1 (Ben), column 2, which is 81. Java gave no error, because both indices happen to be valid. That is what makes this mistake dangerous: the program runs and prints a wrong number. The fix is to put the row first: scores[2][1], which prints Cy, quiz 1: 91. When you write an access, say “row, then column” to yourself and check each index against the grid.

Pause and try

Question 3 · Tracing

Write the exact output. Draw the grid first, then update it line by line.

int[][] tiles = { {4, 7, 2},
                  {9, 5, 8} };
tiles[1][0] = tiles[0][1] + tiles[1][2];
tiles[0][2] *= 3;
System.out.println(tiles[1][0]);
System.out.println(tiles[0][2]);
System.out.println(tiles[1][1] - tiles[0][0]);
Check your reasoning
15
6
1

The starting grid has 2 rows and 3 columns:

Col 0 Col 1 Col 2
Row 0 4 7 2
Row 1 9 5 8
Line What happens Changed element
3 tiles[0][1] is 7 and tiles[1][2] is 8, so 7 + 8 tiles[1][0] = 15
4 tiles[0][2] = tiles[0][2] * 3, so 2 × 3 tiles[0][2] = 6
5 Prints tiles[1][0] prints 15
6 Prints tiles[0][2] prints 6
7 tiles[1][1] is 5 and tiles[0][0] is 4, so 5 − 4 prints 1

Reread 4.11.2 Reading and Changing One Element if your output differed.

Question 4 · Multiple choice

Using the quiz table scores from this chapter, which expression gives Ben’s score (row 1) on the last quiz (column 3), which is 88?

  1. scores[1][3]
  2. scores[3][1]
  3. scores[2][3]
  4. scores[1, 3]
Check your reasoning

A. The first index is the row and the second is the column, both counted from 0. Ben is row 1 and the last quiz is column 3, so scores[1][3] is 88.

  • B is wrong: it puts the column first. Row 3 does not exist (the rows are 0, 1, and 2), so this access throws an ArrayIndexOutOfBoundsException when it runs.
  • C is wrong: it counts rows from 1, treating Ben as row 2. Row 2 is Cy, so this gives Cy’s 100.
  • D is wrong: Java does not accept two indices inside one pair of brackets. The code does not compile; the compiler reports ']' expected. Each index needs its own pair of brackets.

Reread 4.11.2 Reading and Changing One Element if you chose differently.

Section 3 of 5

4.11.3 Creating a 2D Array with new

An initializer list is handy when you already know every value. Often you do not. A theater that sells tickets needs a chart of 2 rows and 3 seats per row, with every seat starting out empty, and the program will fill seats in as people buy them. For that you need to create the grid at its full size first and put values in later.

In Topic 4.3 you created an empty 1D array with new int[5]. A 2D array uses the same keyword with two sizes:

boolean[][] taken = new boolean[2][3];
Piece What it means
boolean[][] The type of the variable: an array of boolean[] rows
new Create a new array object
boolean The element type, the type stored in each box
[2] The number of rows (always first)
[3] The number of columns, the length of every row

Java builds an outer array with 2 elements and gives it 2 row arrays of 3 elements each. Nobody has stored anything in those 6 boxes yet, but Java never leaves an element unset. When new creates a 2D array, every element starts with the default value for its type, the same defaults you met with 1D arrays:

Element type Default value
int 0
double 0.0
boolean false
any reference type, such as String null

Java fills in defaults so that reading an element you have not yet assigned always gives a predictable answer instead of leftover garbage from memory.

Key idea

new type[rows][cols] creates a grid with that many rows and columns, and every element starts at the default value for its type. The size is fixed when the array is created.

Worked example: Defaults in four kinds of grid

Each array below is created with new and never assigned, so every print shows a default.

int[][] counts = new int[2][3];
double[][] prices = new double[2][2];
boolean[][] taken = new boolean[3][4];
String[][] names = new String[2][5];
System.out.println(counts[1][2]);
System.out.println(prices[0][1]);
System.out.println(taken[2][3]);
System.out.println(names[0][4]);
0
0.0
false
null
  • Line 1 makes a 2-by-3 grid of int. Line 5 reads row 1, column 2, the last box, and gets 0.
  • Line 2 makes a 2-by-2 grid of double. The default prints as 0.0, because it is a double.
  • Line 3 makes a 3-by-4 grid of boolean. Row 2, column 3 is false.
  • Line 4 makes a 2-by-5 grid of String. Each box will someday hold a reference to a String, but right now it holds null, meaning “no object yet.” Printing null shows the word null.

The sizes inside the brackets do not have to be literal numbers. Any int expression works, so a program can use variables. Here the theater stores its seat chart and sells two seats:

int rows = 2;
int seatsPerRow = 3;
boolean[][] taken = new boolean[rows][seatsPerRow];
taken[0][1] = true;
taken[1][2] = true;
System.out.println(taken[0][1]);
System.out.println(taken[0][2]);
System.out.println(taken[1][2]);
true
false
true

Line 3 creates a 2-by-3 grid because rows is 2 and seatsPerRow is 3. All six seats start false, meaning not taken. Lines 4 and 5 store true in two of them. Line 7 reads a seat that was never assigned, so it still holds its default, false.

Once the grid exists, its size cannot change. There is no statement that adds a row or makes the rows longer. This comes straight from 1D arrays: the outer array and every row are ordinary arrays, and an array’s length is set when it is created. If the theater adds a row, the program must create a new, larger 2D array.

Watch out

Calling a method on an empty String box. In a grid of a reference type, every box starts as null. A student stores one name and then asks for the length of a name in a box that was never filled:

NullSeat.java
public class NullSeat
{
    public static void main(String[] args)
    {
        String[][] seats = new String[2][3];
        seats[0][0] = "Ana";
        System.out.println(seats[0][0].length());
        System.out.println(seats[0][1].length());
    }
}
3
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because ... is null

Line 7 works: seats[0][0] refers to "Ana", and its length is 3. Line 8 crashes. seats[0][1] was never assigned, so it holds null, and as you saw in Topic 1.14, calling a method on null throws a NullPointerException. Students expect an empty string, "", but new does not create any String objects at all; it only creates the boxes. (The exact message varies between Java versions.)

The fix is to check for null before calling a method, using an if statement from Topic 2.3:

NullSeatFixed.java
public class NullSeatFixed
{
    public static void main(String[] args)
    {
        String[][] seats = new String[2][3];
        seats[0][0] = "Ana";
        System.out.println(seats[0][0].length());
        if (seats[0][1] != null)
        {
            System.out.println(seats[0][1].length());
        }
        else
        {
            System.out.println("Seat is empty");
        }
    }
}
3
Seat is empty

Pause and try

Question 5 · Multiple choice

What does this code print?

boolean[][] lights = new boolean[2][2];
lights[0][1] = true;
System.out.println(lights[1][1]);
  1. false
  2. true
  3. null
  4. 0
Check your reasoning

A. new fills a boolean grid with the default false. Only row 0, column 1 was assigned, so row 1, column 1 still holds false, and line 3 prints it.

  • B is wrong: assigning true to one box changes only that box (row 0, column 1), not the whole row or column.
  • C is wrong: null is the default only for reference types such as String. boolean is a primitive.
  • D is wrong: 0 is the default for int. A boolean can only be true or false.

Reread 4.11.3 Creating a 2D Array with new if you chose differently.

Question 6 · Tracing

Write the exact output.

double[][] fees = new double[3][2];
fees[2][1] = 12.5;
fees[0][0] = fees[2][1] * 2;
System.out.println(fees[0][0]);
System.out.println(fees[1][1]);
System.out.println(fees[2][1] + fees[2][0]);
Check your reasoning
25.0
0.0
12.5

Line 1 creates 3 rows and 2 columns of double, all 0.0.

Line What happens Printed
2 fees[2][1] becomes 12.5
3 fees[2][1] * 2 is 25.0, stored in fees[0][0]
4 Prints fees[0][0] 25.0
5 fees[1][1] was never assigned: default 0.0
6 12.5 + 0.0 (row 2, column 0 is still the default) 12.5

Values are double, so each prints with a decimal point.

Reread 4.11.3 Creating a 2D Array with new if your output differed.

Section 4 of 5

4.11.4 One Row Is a 1D Array

Suppose a method already exists that works on one student’s scores, an int[], the kind of method you could have written in Topic 4.4. Can you hand it Ana’s row from the 2D table, or do you have to copy her four scores into a new array first?

Look at the array-of-arrays picture again. Each element of the outer array is a 1D array. So using the 2D array’s name with only one pair of brackets, scores[1], gives you the element at index 1 of the outer array: Ben’s whole row. Its type is int[], and you can use it anywhere an int[] is allowed.

Expression Brackets What it names Type
scores none the whole grid int[][]
scores[1] one row 1, a 1D array int[]
scores[1][2] two one element, row 1 column 2 int

Each pair of brackets peels off one level: from grid to row, then from row to a single number.

There is one more thing to remember from Topic 1.13 and Topic 4.3: arrays are objects, and a variable of an array type holds a reference. scores[1] does not make a copy of Ben’s row. It gives you a reference to the very row that lives inside the grid. If you store it in a variable, that variable and the grid share the row.

Key idea

grid[r], with one pair of brackets, is row r of the grid, a 1D array. It is the same row object that is inside the grid, not a copy, so changing an element through it changes the grid.

Worked example: Taking out Cy's row

Line 4 stores row 2 in a 1D array variable. Watch what line 7 does to the grid.

int[][] scores = { {90, 85, 77, 92},
                   {68, 74, 81, 88},
                   {95, 91, 89, 100} };
int[] cyRow = scores[2];
System.out.println(cyRow[1]);
System.out.println(cyRow.length);
cyRow[0] = 99;
System.out.println(scores[2][0]);
91
4
99
  • Line 4: scores[2] is a reference to Cy’s row. cyRow now refers to that same row array, [95, 91, 89, 100].
  • Line 5: cyRow[1] is an ordinary 1D access, index 1 of Cy’s row: 91. It is the same element as scores[2][1].
  • Line 6: cyRow.length is the length of the row, 4, the number of columns.
  • Line 7 stores 99 at index 0 of the row cyRow refers to.
  • Line 8 reads scores[2][0]. scores[2] leads to the very row that line 7 changed, so it prints 99, not 95.
scores --> outer array
           index 0 --> [ 90 | 85 | 77 | 92  ]
           index 1 --> [ 68 | 74 | 81 | 88  ]
           index 2 --> [ 99 | 91 | 89 | 100 ]  <-- cyRow

There is only one row 2. cyRow and scores[2] are two ways to reach it.

The real payoff is passing a row to a method. The method firstAndLast below has an int[] parameter. It has no idea that its argument came from a 2D array.

RowToMethod.java
public class RowToMethod
{
    public static String firstAndLast(int[] quizzes)
    {
        return quizzes[0] + " to " + quizzes[quizzes.length - 1];
    }

    public static void main(String[] args)
    {
        int[][] scores = { {90, 85, 77, 92},
                           {68, 74, 81, 88},
                           {95, 91, 89, 100} };
        System.out.println("Ana: " + firstAndLast(scores[0]));
        System.out.println("Ben: " + firstAndLast(scores[1]));
    }
}
Ana: 90 to 92
Ben: 68 to 88

On line 13, the argument scores[0] is Ana’s row, so the parameter quizzes gets a reference to that row (Topic 3.6). Inside the method, quizzes.length is 4, so quizzes[quizzes.length - 1] is quizzes[3], the last score, 92. Line 14 passes Ben’s row instead, and the same method reports 68 and 88. One method written for 1D arrays now works on any row.

Watch out

Storing a row in an int variable. A student wants Ben’s row and writes:

RowWrongType.java
public class RowWrongType
{
    public static void main(String[] args)
    {
        int[][] scores = { {90, 85, 77, 92},
                           {68, 74, 81, 88},
                           {95, 91, 89, 100} };
        int benRow = scores[1];
        System.out.println(benRow);
    }
}
RowWrongType.java:8: error: incompatible types: int[] cannot be converted to int
        int benRow = scores[1];
                           ^

With one pair of brackets, scores[1] is a whole row, an int[], but benRow was declared to hold a single int. Students make this mistake because in a 1D array one pair of brackets always gives one element. In a 2D array, one pair gives a row and you need two pairs to reach an element. Fix it by matching the type to what you want: int[] benRow = scores[1]; for the row, or int benScore = scores[1][2]; for one score.

Pause and try

Question 7 · Multiple choice

Given double[][] temps = new double[3][5];, what is the type of the expression temps[2]?

  1. double[]
  2. double
  3. double[][]
  4. int
Check your reasoning

A. One pair of brackets on a 2D array picks one element of the outer array, and each of those elements is a row. A row of a double grid is a double[].

  • B is wrong: a single double needs two indices, such as temps[2][4]. This is the 1D habit of “one bracket, one element.”
  • C is wrong: double[][] is the type of temps itself, with no brackets. Each pair of brackets removes one level.
  • D is wrong: the index 2 is an int, but the expression gives what is stored at that index, which is a row of double values.

Reread 4.11.4 One Row Is a 1D Array if you chose differently.

Question 8 · Tracing

Write the exact output.

int[][] stock = { {5, 0, 3},
                  {2, 8, 6} };
int[] top = stock[0];
top[1] = 4;
stock[0][2] += top[0];
System.out.println(stock[0][1]);
System.out.println(top[2]);
System.out.println(stock[1].length);
Check your reasoning
4
8
3

top refers to row 0 of stock; it is not a copy.

Line What happens Row 0 Row 1
1–2 Grid created [5, 0, 3] [2, 8, 6]
3 top refers to row 0 [5, 0, 3] [2, 8, 6]
4 top[1] = 4 changes row 0 [5, 4, 3] [2, 8, 6]
5 stock[0][2] += top[0], so 3 + 5 [5, 4, 8] [2, 8, 6]
6 stock[0][1] is 4
7 top[2] is the same element as stock[0][2]: 8
8 stock[1].length is the length of row 1: 3

If you printed 0 for line 6 or 3 for line 7, you treated top as a separate copy of the row.

Reread 4.11.4 One Row Is a 1D Array if your output differed.

Section 5 of 5

4.11.5 Counting Rows and Columns

A method that receives a 2D array often has no idea how big it is. The quiz table might have 3 students or 30. To reach the last student’s last score, or just to avoid using an index that does not exist, code needs to ask the grid for its size.

A 1D array tells you its size through length (Topic 4.3). A 2D array has no separate “rows” or “columns” attribute; it does not need one, because the array-of-arrays picture gives both numbers:

  • The outer array has one element per row, so scores.length is the number of rows.
  • Each row is a 1D array whose length is the number of columns, so scores[0].length is the number of columns.

Row 0 is used because it is always there, and in this course every row has the same length, so any row would give the same answer.

The same counts give the valid indices. Like any array, indices run from 0 up to one less than the length:

Index Valid values For the quiz table
row index 0 to scores.length - 1 0, 1, 2
col index 0 to scores[0].length - 1 0, 1, 2, 3

An index outside these ranges throws an ArrayIndexOutOfBoundsException, the same exception as a bad index in a 1D array, and for the same reason: one of the two 1D accesses inside grid[r][c] asked for a box that does not exist.

Key idea

For a 2D array grid, grid.length is the number of rows and grid[0].length is the number of columns. Valid row indices are 0 through grid.length - 1, valid column indices are 0 through grid[0].length - 1, and anything else throws an ArrayIndexOutOfBoundsException.

Worked example: Finding the last score

Lines 4 and 5 compute the last valid row and column from the lengths, instead of counting by hand.

int[][] scores = { {90, 85, 77, 92},
                   {68, 74, 81, 88},
                   {95, 91, 89, 100} };
int lastRow = scores.length - 1;
int lastCol = scores[0].length - 1;
System.out.println(scores.length);
System.out.println(scores[0].length);
System.out.println(scores[lastRow][lastCol]);
3
4
100
Line Expression Why Value
4 scores.length - 1 3 rows, so the last row index is 3 − 1 2
5 scores[0].length - 1 row 0 has 4 elements, so last column is 3 3
6 scores.length elements in the outer array, one per row 3
7 scores[0].length elements in row 0 4
8 scores[lastRow][lastCol] scores[2][3], Cy’s last quiz 100

If the teacher adds a fourth student row to the initializer list, lines 4 and 5 adjust by themselves, and line 8 still finds the bottom-right score.

Now change the shape. This grid, made with new, has more rows than columns.

int[][] board = new int[5][2];
board[board.length - 1][board[0].length - 1] = 7;
System.out.println(board.length);
System.out.println(board[0].length);
System.out.println(board[4][1]);
5
2
7

new int[5][2] makes 5 rows of 2 columns, so board.length is 5 and board[0].length is 2. Line 2 stores 7 at board[4][1], the bottom-right box: row index 4 (5 − 1) and column index 1 (2 − 1). When a grid is not square, mixing up the two lengths gives a different answer, which the next Watch Out shows.

Watch out

Using the wrong length. A student wants Ana’s last quiz and uses scores.length for the column:

int[][] scores = { {90, 85, 77, 92},
                   {68, 74, 81, 88},
                   {95, 91, 89, 100} };
System.out.println("Ana's last quiz: " + scores[0][scores.length - 1]);
Ana's last quiz: 77

The answer should be 92. scores.length is the number of rows, 3, so scores.length - 1 is 2, and the program read column 2. The quiz table has 3 rows but 4 columns, so the two lengths are not interchangeable. On a square grid the bug would hide, which is why it slips through. Use scores[0].length - 1 for the last column.

The other common slip forgets the - 1:

PastLastColumn.java
public class PastLastColumn
{
    public static void main(String[] args)
    {
        int[][] scores = { {90, 85, 77, 92},
                           {68, 74, 81, 88},
                           {95, 91, 89, 100} };
        System.out.println("Ben's last quiz:");
        System.out.println(scores[1][scores[0].length]);
    }
}
Ben's last quiz:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 4 out of bounds for length 4

Line 9 asks for column scores[0].length, which is 4, but the columns are 0 through 3. Java throws an ArrayIndexOutOfBoundsException when the program runs. The message names the bad index (4) and the length of the array it was used on (Ben’s row, length 4); the wording can vary between Java versions. Writing scores[1][scores[0].length - 1] prints 88.

Pause and try

Question 9 · Multiple choice

Given String[][] menu = new String[4][6];, which describes all the valid indices for menu[r][c]?

  1. r from 0 to 3 and c from 0 to 5
  2. r from 1 to 4 and c from 1 to 6
  3. r from 0 to 4 and c from 0 to 6
  4. r from 0 to 5 and c from 0 to 3
Check your reasoning

A. menu.length is 4 rows and menu[0].length is 6 columns. Indices start at 0 and end at length − 1, so rows are 0–3 and columns are 0–5.

  • B is wrong: it counts from 1. Index 0 is valid, and index 4 for a row would throw an ArrayIndexOutOfBoundsException.
  • C is wrong: it forgets the - 1. Index 4 for the row (or 6 for the column) is one past the end.
  • D is wrong: it swaps rows and columns. The first size in new String[4][6] is the number of rows.

Reread 4.11.5 Counting Rows and Columns if you chose differently.

Question 10 · Tracing

Write the exact output.

int[][] marks = new int[2][5];
marks[marks.length - 1][0] = marks[0].length;
marks[0][marks[0].length - 1] = marks.length * 10;
System.out.println(marks[1][0]);
System.out.println(marks[0][4]);
System.out.println(marks[1][4]);
Check your reasoning
5
20
0

marks has 2 rows (marks.length is 2) and 5 columns (marks[0].length is 5), all starting at 0.

Line Index expressions Becomes Assigned value
2 marks[2 - 1][0] marks[1][0] marks[0].length, 5
3 marks[0][5 - 1] marks[0][4] marks.length * 10, 20
4–6 Prints marks[1][0], marks[0][4], marks[1][4] 5, 20, and the default 0

Reread 4.11.5 Counting Rows and Columns if your output differed.

Wrap-up · 1 of 6

Putting it together

This program runs a small ticket booth. It creates a seat chart with new, fills seats using indices, uses length to find the last row and column, changes a seat through a row variable, and checks for an empty seat before treating it as a name.

TicketBooth.java
public class TicketBooth
{
    public static void main(String[] args)
    {
        String[][] seats = new String[3][4];
        seats[0][0] = "Ana";
        seats[1][3] = "Ben";
        seats[seats.length - 1][0] = "Cy";
        String[] middle = seats[1];
        middle[1] = "Dee";
        System.out.println("Rows: " + seats.length);
        System.out.println("Seats per row: " + seats[0].length);
        System.out.println("Row 1, seat 1: " + seats[1][1]);
        System.out.println("Row 2, seat 0: " + seats[2][0]);
        System.out.println("Last seat: " + seats[seats.length - 1][seats[0].length - 1]);
        if (seats[0][2] == null)
        {
            System.out.println("Row 0, seat 2 is open");
        }
    }
}

The chart has 3 rows and 4 seats per row, and every box starts as null.

Line(s) What happens Seats filled so far
5 new String[3][4] makes 12 boxes, all null none
6 Row 0, seat 0 gets "Ana" [0][0] Ana
7 Row 1, seat 3 gets "Ben" + [1][3] Ben
8 seats.length - 1 is 2, the last row; row 2, seat 0 gets "Cy" + [2][0] Cy
9 middle refers to row 1 itself, not a copy (no change)
10 middle[1] is the same box as seats[1][1]; it gets "Dee" + [1][1] Dee
11–12 seats.length is 3 rows; seats[0].length is 4 seats per row
13 seats[1][1] was set through middle
14 seats[2][0] was set on line 8
15 seats[2][3], the bottom-right seat, was never assigned, so it is null
16–19 seats[0][2] is still null, so the condition is true
Rows: 3
Seats per row: 4
Row 1, seat 1: Dee
Row 2, seat 0: Cy
Last seat: null
Row 0, seat 2 is open

Line 15 prints the word null by joining it to a string, which is safe; only calling a method on null would crash. Line 16 avoids that kind of crash by comparing with null before using the seat.

Wrap-up · 2 of 6

Common mistakes

Each of these mistakes was taught in its own subsection. This table lets you look up a message you see when your own code goes wrong.

What you see Likely cause Fix
illegal initializer for int (compile error) A 2D initializer list stored in a 1D type such as int[] Declare the variable as int[][]
incompatible types: int[] cannot be converted to int One pair of brackets (a whole row) stored in an int variable Use int[] for a row, or add the column index for one element
ArrayIndexOutOfBoundsException (runtime) An index equal to a length, a missing - 1, or row and column swapped Keep rows in 0 to grid.length - 1, columns in 0 to grid[0].length - 1
NullPointerException (runtime) A method called on an unfilled box of a reference-type grid Assign the box first, or check != null
Wrong value, no error Row and column swapped, or grid.length used for columns Row first, then column; columns come from grid[0].length
Wrap-up · 3 of 6

Vocabulary

Term Definition Example
two-dimensional (2D) array An array whose elements are 1D arrays of the same type, pictured as a grid int[][] scores
array of arrays How Java stores a 2D array: an outer array whose elements refer to row arrays scores refers to 3 row arrays
row One inner array of a 2D array, a line across the grid scores[1] is {68, 74, 81, 88}
column The elements at the same index in every row, a line down the grid column 2 of scores is 77, 81, 89
row index The first index in grid[r][c], which picks the row the 1 in scores[1][2]
column index The second index in grid[r][c], which picks the column the 2 in scores[1][2]
default value (review) The value new puts in every element: 0, 0.0, false, or null depending on the type new int[2][3] holds six 0s
Wrap-up · 4 of 6

Chapter summary

  • A 2D array is an array of arrays: an outer array whose elements are 1D row arrays of the same type. Picture it as a grid of rows and columns, both numbered from 0. It can hold primitives or object references, and in this course every row has the same length.
  • Create a 2D array with an initializer list of row lists, such as { {1, 2, 3}, {4, 5, 6} }, or with new type[rows][cols], which fills every element with its default value: 0, 0.0, false, or null. The size cannot change after creation.
  • grid[r][c] reads or changes one element. The first index is always the row and the second is always the column.
  • grid[r], with one pair of brackets, is row r as a 1D array. It is the same row object as the one inside the grid, so changes made through it show up in the grid.
  • grid.length is the number of rows and grid[0].length is the number of columns. Indices outside 0 to length − 1 throw an ArrayIndexOutOfBoundsException.
Wrap-up · 5 of 6

Review questions

Question 11 · Multiple choice

A weather program creates double[][] rainfall = new double[4][7]; to store 4 weeks (rows) of 7 days (columns). Which statement is true right after this line runs?

  1. rainfall has 7 rows and 4 columns.
  2. rainfall[3][6] holds 0.0.
  3. rainfall[4][7] is the last element.
  4. rainfall.length is 28.
Check your reasoning

B. The first size is the number of rows, so the grid has 4 rows and 7 columns. The last valid indices are 3 and 6, and new filled every double element with 0.0. So rainfall[3][6] exists and holds 0.0.

  • A is wrong: it reverses the sizes. In new double[4][7], the 4 is rows.
  • C is wrong: indices end at length − 1. Row 4 and column 7 are both one past the end, so rainfall[4][7] throws an ArrayIndexOutOfBoundsException.
  • D is wrong: rainfall.length counts the elements of the outer array, which is one per row: 4. The 28 elements are spread across the rows.

Reread 4.11.3 Creating a 2D Array with new and 4.11.5 Counting Rows and Columns if you chose differently.

Question 12 · Tracing

Write the exact output.

int[][] grid = { {3, 1, 4, 1},
                 {5, 9, 2, 7} };
int[] second = grid[1];
grid[0][grid.length] = second[3];
second[0] -= grid[0][0];
System.out.println(grid[0][2]);
System.out.println(grid[1][0]);
System.out.println(grid.length + grid[1].length);
Check your reasoning
7
2
6

grid has 2 rows and 4 columns. second refers to row 1 itself.

Line What happens Row 0 Row 1
1–2 Grid created [3, 1, 4, 1] [5, 9, 2, 7]
3 second refers to row 1 [3, 1, 4, 1] [5, 9, 2, 7]
4 grid.length is 2 (rows), so this is grid[0][2] = second[3], which is 7 [3, 1, 7, 1] [5, 9, 2, 7]
5 second[0] = 5 - grid[0][0], so 5 − 3 = 2, stored in row 1 itself [3, 1, 7, 1] [2, 9, 2, 7]
6 grid[0][2] prints 7
7 grid[1][0] is the box line 5 changed through second prints 2
8 grid.length + grid[1].length is 2 + 4 prints 6

Line 4 uses a row count as a column index. That is legal here because 2 is a valid column, but it is the kind of length mix-up to watch for.

Reread 4.11.4 One Row Is a 1D Array and 4.11.5 Counting Rows and Columns if your output differed.

Question 13 · Debugging

This program should print the bottom-right temperature, 69.0. Instead it prints Last reading: and then crashes with an ArrayIndexOutOfBoundsException. Explain the error and correct line 8.

BottomRight.java
public class BottomRight
{
    public static void main(String[] args)
    {
        double[][] temps = { {61.5, 70.0, 66.5},
                             {58.0, 73.5, 69.0} };
        System.out.println("Last reading:");
        System.out.println(temps[temps.length][temps[0].length]);
    }
}
Check your reasoning

temps.length is 2 and temps[0].length is 3, so line 8 asks for temps[2][3]. The valid rows are 0 and 1, and the valid columns are 0, 1, and 2. Row index 2 is one past the last row, so Java throws the exception as soon as it tries to find row 2, before it even looks at the column. A length counts the elements; the last index is always one less.

Corrected line 8:

System.out.println(temps[temps.length - 1][temps[0].length - 1]);

This reads temps[1][2], and the program prints Last reading: and then 69.0.

Reread 4.11.5 Counting Rows and Columns if your fix differed.

Question 14 · Short answer

A student asks: “Why is the number of columns grid[0].length and not something like grid.columns? And why row 0? Would grid[1].length work too?” Answer the student in two or three sentences, using the idea of an array of arrays.

Check your reasoning

A 2D array is an outer array whose elements are row arrays. grid.length counts the outer array’s elements, one per row, and grid[0] is the first row, a 1D array, so its length is the number of elements in a row, which is the number of columns. Row 0 is used because it is always there, but every row in this course has the same length, so grid[1].length (when the grid has at least 2 rows) gives the same answer.

A complete answer mentions that grid[0] is a row (a 1D array), that a row’s length is the column count, and that all rows have equal length here, so any existing row works.

Reread 4.11.1 A Grid Is an Array of Arrays and 4.11.5 Counting Rows and Columns if your answer differed.

Question 15 · Multiple choice

What does this code print?

int[][] nums = { {2, 4},
                 {6, 8},
                 {10, 12} };
System.out.println(nums[2][1] + nums.length * nums[0].length);
  1. 18
  2. 12
  3. 24
  4. Nothing; it throws an ArrayIndexOutOfBoundsException because nums has only 2 columns
Check your reasoning

A. The initializer list has 3 inner lists of 2 values, so nums.length is 3 and nums[0].length is 2. nums[2][1] is row 2, column 1: 12. Multiplication happens before addition, so the expression is 12 + 3 × 2 = 18.

  • B is wrong: it counts from 1, reading nums[2][1] as “second row, first column,” which is 6, and then 6 + 6 = 12.
  • C is wrong: it treats nums.length as 6, the total number of values, giving 12 + 6 × 2 = 24. length on the outer array counts rows, so it is 3.
  • D is wrong: it reads [2] as the column. The first index is the row, and row 2 exists (rows 0 to 2). The column index 1 is valid too.

Reread 4.11.2 Reading and Changing One Element and 4.11.5 Counting Rows and Columns if you chose differently.

Question 16 · Debugging

A school tracks absences for 3 school days (rows) and 4 class periods (columns), as the comment says. The program crashes with an ArrayIndexOutOfBoundsException before printing anything. Find the error and correct the line that causes it.

AttendanceChart.java
public class AttendanceChart
{
    public static void main(String[] args)
    {
        // 3 school days (rows) by 4 class periods (columns)
        int[][] absences = new int[4][3];
        absences[0][1] = 2;
        absences[2][3] = 1;
        System.out.println(absences[0][1] + absences[2][3]);
    }
}
Check your reasoning

Line 6 creates the array with the sizes reversed. new int[4][3] makes 4 rows of 3 columns, so the valid column indices are only 0, 1, and 2. Line 7 is fine, but line 8 uses column 3, which does not exist, and Java throws the exception there. The student wrote the sizes as “4 periods, 3 days,” but the first size is always the number of rows.

Corrected line 6:

int[][] absences = new int[3][4];

Now there are 3 rows (days) and 4 columns (periods), absences[2][3] is the last box of the last row, and the program prints 3.

Changing line 8 to a valid index would also stop the crash, but it would record the absence in the wrong place; the real error is the shape of the array.

Reread 4.11.3 Creating a 2D Array with new if your fix differed.

Wrap-up · 6 of 6

Reflection

  • I can explain why a 2D array is called an array of arrays and draw one as a grid of rows and columns. Reread
  • I can read a 2D initializer list and say how many rows and columns it creates. Reread
  • I can read and change one element with two indices, row first and column second. Reread
  • I can create a 2D array with new and say what default value every element starts with. Reread
  • I can use one pair of brackets to get a row and explain why changing that row changes the grid. Reread
  • I can find the number of rows and columns with length and name every valid index. Reread