Learn Java

Lesson 8Phase 1: First steps

Arrays, two dimensions, and practice

Grids and tables, nested loops over rows and columns, and the ten one-dimensional problems.

By Updated 60 min

  • 2D array
  • rows and columns
  • nested loops
  • grid traversal
  • array problems

A one-dimensional array is a row of boxes. That was enough to hold a list of test scores or a week of temperatures. But plenty of data comes as a grid: a chessboard, a spreadsheet, the pixels on a screen, the seats in a cinema. For those you want rows and columns at once. That is what a two-dimensional array gives you.

By the end of this lesson
  • Picture a 2D array as a grid of rows and columns, and read or change one cell with grid[r][c].
  • Find how many rows a grid has with grid.length, and how many columns a row has with grid[r].length.
  • Visit every cell with nested loops, row by row or column by column, and predict the order of the visits.
  • Make an empty grid with new int[rows][cols], fill it with a loop, and print it.
  • Explain why a 2D array is an array of arrays, and why that lets rows have different lengths.
  • Add up the rows, the columns and the two diagonals of a grid.
From before
This lesson stands on one-dimensional arrays and loops. If indexing from zero, .length and walking an array with a for loop feel solid, you are ready. The loops here count with r++, which adds one to r just as r = r + 1 does. The one new idea is putting arrays inside an array.

A quick warm-up on one dimension#

Before adding a second dimension, check that the first one is steady. Here is the pattern you already know: declare an array, walk it with a loop, and add the values into a running total. Press Run and step through it, watching total grow.

Warmup.java
1public class Main {
2 public static void main(String[] args) {
3 int[] row = {10, 20, 30};
4 int total = 0;
5
6 for (int i = 0; i < row.length; i++) {
7 total = total + row[i];
8 }
9
10 System.out.println("Sum is " + total);
11 }
12}
Prints
›Sum is 60

The loop ran three times, with i taking the values 0, 1 and 2, and it printed Sum is 60. Hold on to the shape of that loop. Most of this lesson puts one of these loops inside another.

A grid: rows and columns#

Suppose you want to store a small table of numbers, two rows of three:

Grid.java
1public class Main {
2 public static void main(String[] args) {
3 int[][] grid = {
4 {1, 2, 3},
5 {4, 5, 6}
6 };
7
8 System.out.println("Top left is " + grid[0][0]);
9 System.out.println("Bottom right is " + grid[1][2]);
10
11 grid[0][1] = 20;
12 System.out.println("Row 0, column 1 is now " + grid[0][1]);
13 }
14}
Prints
›Top left is 1
›Bottom right is 6
›Row 0, column 1 is now 20

Read the type slowly. int[] was “an array of int”. Now int[][]is “an array of arrays of int”, with two pairs of brackets because there are two dimensions. The literal has braces inside braces: the outer braces hold the whole grid, and each inner pair of braces is one row.

To reach a single cell you give two indexes. The first picks the row, the second picks the column inside that row, and both count from zero. So grid[0][0] is the very first cell, the 1 in the top-left corner. grid[1][2] is row 1 (the second row) and column 2 (the third number in it), which is the 6.

The same two indexes on the left of = change a cell. grid[0][1] = 20; replaces the 2 in row 0, and the last line of output shows the new value.

Here is the same grid drawn as rows and columns, with every index written in. Tap any cell to see the name Java uses for it. Then switch to the grid with three rows and find grid[2][0], which only exists once there is a row 2.

A grid, cell by cellint[][] grid
Grid:
col 0col 1col 2row 0row 1

Tap any cell to see the name Java uses for it.

Key idea

A cell is named by two numbers: grid[row][column]. Row first, then column, and both start at zero. Swapping the two gives you a different cell, or an error.

Row first, then column
A common slip is writing grid[column][row], especially if you are used to giving a point as (x, y), across and then down. In this grid, the 3 at the end of row 0 is grid[0][2]. Written column first, as grid[2][0], it asks for row 2, which does not exist, so Java stops with ArrayIndexOutOfBoundsException: Index 2 out of bounds for length 2. Say it to yourself the same way every time: “row, then column”.
Quick check
In int[][] grid = {{1, 2, 3}, {4, 5, 6}};, which expression gives you the 6?

How big is the grid? length in two directions#

With a 1D array, row.length told you how many boxes it had. A grid raises two questions: how many rows, and how many columns. The answer reuses .length; you just ask it of the right thing.

  • grid.length is the number of rows. It asks the outer array how many inner arrays it holds.
  • grid[r].length is the number of columns in row r. It asks one particular row how many numbers it holds.
Sizes.java
1public class Main {
2 public static void main(String[] args) {
3 int[][] grid = {
4 {1, 2, 3},
5 {4, 5, 6}
6 };
7
8 System.out.println("Rows: " + grid.length);
9 System.out.println("Columns in row 0: " + grid[0].length);
10 System.out.println("Columns in row 1: " + grid[1].length);
11 }
12}
Prints
›Rows: 2
›Columns in row 0: 3
›Columns in row 1: 3

grid.length is 2 (two rows) and grid[0].length is 3 (three columns). The plain grid.length never tells you the column count, because the outer array only knows how many rows there are. To count columns you first step into a row.

Why ask each row separately?
It looks like extra work to write grid[r].lengthinstead of one fixed number. The reason is that Java does not promise every row has the same length. Most of the time they do, and a neat rectangle is what you will usually build. But each row is its own array, so the language lets them differ, and the reliable way to ask for a row's width is to ask that row. You meet the uneven case later in this lesson.
Quick check
What does this print? int[][] t = {{1, 2}, {3, 4}, {5, 6}}; followed by System.out.println(t.length + " " + t[0].length);

Visiting every cell with nested loops#

To touch every box in a 1D array you used one loop. A grid has rows and columns, so you use one loop inside another. The outer loop walks the rows. For each row it enters, the inner loop walks every column of that row. The inner loop finishes completely before the outer loop moves to the next row.

  1. 1The outer loop sets r = 0 (the first row).
  2. 2The inner loop runs fully: c = 0, then 1, then 2, visiting every cell in row 0.
  3. 3The outer loop moves on: r = 1 (the second row).
  4. 4The inner loop runs fully again for row 1: c = 0, 1, 2.
  5. 5The outer loop has no more rows, so the whole thing stops.

That is six visits in total, two rows times three columns, which is exactly the number of cells. Here is the pattern in code, summing the whole grid into one total. Press Run and step through it: for each value of r, c runs through 0, 1 and 2. To watch the same order on a drawn grid, choose Row by row in the grid widget above and press Play.

SumGrid.java
1public class Main {
2 public static void main(String[] args) {
3 int[][] grid = {
4 {1, 2, 3},
5 {4, 5, 6}
6 };
7
8 int total = 0;
9
10 for (int r = 0; r < grid.length; r++) {
11 for (int c = 0; c < grid[r].length; c++) {
12 total = total + grid[r][c];
13 }
14 }
15
16 System.out.println("The grid sums to " + total);
17 }
18}
Prints
›The grid sums to 21

Trace the visits in order and you add 1 + 2 + 3 from the first row, giving 6, then 4 + 5 + 6 from the second, giving another 15. Together that is 21, exactly what it prints. The shape to memorise is the bounds: the outer loop stops at grid.length (the number of rows) and the inner loop stops at grid[r].length (the columns in the row it is on).

Name your loop variables after what they walk
Writing r for row and c for column, rather than i and j, makes grid[r][c] read like English: “the cell at row r, column c”. With i and j it is far too easy to flip them by mistake and read the wrong cell.

Printing the grid as a table

A useful trick: inside the inner loop, print each value with a space after it. Then, after the inner loop finishes one row, call println with nothing in it to drop to the next line. The inner loop draws a row from left to right; the empty println ends the row.

PrintGrid.java
1public class Main {
2 public static void main(String[] args) {
3 int[][] grid = {
4 {1, 2, 3},
5 {4, 5, 6}
6 };
7
8 for (int r = 0; r < grid.length; r++) {
9 for (int c = 0; c < grid[r].length; c++) {
10 System.out.print(grid[r][c] + " ");
11 }
12 System.out.println();
13 }
14 }
15}
Prints
›1 2 3
›4 5 6

Watch where each statement sits. System.out.print is inside the inner loop, so it runs once per cell and keeps the cursor on the same line. The empty System.out.println()is inside the outer loop but outside the inner one, so it runs once per row, after all of that row's cells have been printed, and starts a fresh line. Move that println inside the inner loop and every number would land on its own line instead.

Put the lines in orderSeats.java
A cinema marks each seat 1 (taken) or 0 (free). Put the lines in order so the program prints the seating plan as a table: 1 0 1 1 on the first line and 0 0 1 0 on the second.

Tap a line, then tap where it should go. Or use the arrows.

ExerciseBoard.java
A tic-tac-toe board is stored as a grid of char, with '.' for an empty square. Finish the program so it prints the row and column of every empty square, as Empty: row 1, column 0, and counts them. Visit the squares row by row, so the empty squares come out in reading order.

Making an empty grid with new#

Listing every value works when you know them all up front. When you only know the size, ask for an empty grid with new, the way new int[5] made an empty row of five:

EmptyGrid.java
1import java.util.Arrays;
2
3public class Main {
4 public static void main(String[] args) {
5 int[][] board = new int[3][4];
6 System.out.println(Arrays.deepToString(board));
7
8 board[1][2] = 7;
9 System.out.println(Arrays.deepToString(board));
10 System.out.println(board.length + " rows of " + board[0].length);
11 }
12}
Prints
›[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
›[[0, 0, 0, 0], [0, 0, 7, 0], [0, 0, 0, 0]]
›3 rows of 4

new int[3][4] means 3 rows of 4 columns: the row count comes first, in the same order as board[r][c]. Every cell starts at 0, the default for an int, just as in a new one-dimensional array. Then board[1][2] = 7; changes one cell, and the second line of output shows the 7 in the middle row.

Arrays.deepToString turns a whole grid into text, one pair of square brackets per row inside an outer pair. It lives in java.util.Arrays, which is why the program starts with an import line. It is the quick way to check what a grid holds. When you want to choose the layout yourself, use the printing loop from the last section.

A new grid is usually filled by the same nested loop, writing to each cell instead of reading it. This one builds a times table. The cell in row r, column c gets (r + 1) * (c + 1), because the indexes count from 0 and the table counts from 1.

TimesTable.java
1public class Main {
2 public static void main(String[] args) {
3 int[][] times = new int[4][5];
4
5 for (int r = 0; r < times.length; r++) {
6 for (int c = 0; c < times[r].length; c++) {
7 times[r][c] = (r + 1) * (c + 1);
8 }
9 }
10
11 for (int r = 0; r < times.length; r++) {
12 for (int c = 0; c < times[r].length; c++) {
13 System.out.print(times[r][c] + "\t");
14 }
15 System.out.println();
16 }
17 }
18}
Prints
›1 2 3 4 5
›2 4 6 8 10
›3 6 9 12 15
›4 8 12 16 20

The first pair of loops fills all 20 cells; the second pair prints them. Each value is followed by \t, the tab escape from variables and types, which lines the columns up even when a number has two digits.

What a 2D array really is: an array of arrays#

So far this page has called it a grid, and as a mental picture that works well. Underneath, Java has no special grid type. A 2D array is an array whose elements are themselves arrays.

So grid is an array of length 2. Each of its two elements is a whole one-dimensional array of int. grid[0] is the first inner array, {1, 2, 3}. grid[1] is the second, {4, 5, 6}. When you write grid[0][2], you are doing two ordinary array lookups in a row: first grid[0] hands you the array {1, 2, 3}, and then the second [2] indexes into that array to get 3.

The memory view shows this. Run the program below, step through it, and look at the memory view under the code. It holds three arrays: grid, whose two cells each hold a reference to a row (where that row lives in memory), and the two rows themselves, each an ordinary array of three numbers. After int[] top = grid[0]; the first row also goes by the name top.

Rows.java
1public class Main {
2 public static void main(String[] args) {
3 int[][] grid = {
4 {1, 2, 3},
5 {4, 5, 6}
6 };
7
8 int[] top = grid[0];
9
10 System.out.println("grid has " + grid.length + " rows");
11 System.out.println("top has " + top.length + " numbers");
12 System.out.println("top[2] is " + top[2]);
13 }
14}
Prints
›grid has 2 rows
›top has 3 numbers
›top[2] is 3
Key idea

grid[r][c] is two lookups, one after the other. grid[r] selects a row, which is itself an array, and then [c] selects an element inside that row.

Quick check
Using the grid above, a program runs int[] top = grid[0]; and then top[0] = 100;. What is grid[0][0] now?

This one fact explains the two .length rules. grid.length is the length of the outer array, the number of rows. grid[r].lengthis the length of one inner array, the number of columns in that row. There is no special “column length” rule to learn; it is the ordinary length of an ordinary array that happens to live inside another one.

Rows of different lengths#

Because each row is its own array, nothing forces the rows to be the same length. A 2D array whose rows differ in length is called a jagged array. Here is one:

Jagged.java
1public class Main {
2 public static void main(String[] args) {
3 int[][] jagged = {
4 {1, 2},
5 {3, 4, 5},
6 {6}
7 };
8
9 System.out.println("Rows: " + jagged.length);
10
11 for (int r = 0; r < jagged.length; r++) {
12 System.out.println("Row " + r + " has " + jagged[r].length + " columns");
13 }
14 }
15}
Prints
›Rows: 3
›Row 0 has 2 columns
›Row 1 has 3 columns
›Row 2 has 1 columns

The outer array has three rows, so jagged.length is 3. Each row reports its own width: 2, then 3, then 1. This is why a safe loop asks grid[r].length instead of assuming a fixed number. With a fixed inner bound of c < 3, the loop would crash on the very first row, which has only two columns.

Predict the outputLastOfEach.java
1public class Main {
2 public static void main(String[] args) {
3 int[][] jagged = {
4 {1, 2},
5 {3, 4, 5},
6 {6}
7 };
8
9 int total = 0;
10 for (int r = 0; r < jagged.length; r++) {
11 int last = jagged[r].length - 1;
12 total = total + jagged[r][last];
13 }
14
15 System.out.println(total);
16 }
17}

Each pass of this loop adds the last number of one row. What does the program print?

It prints 13. jagged[r].length - 1 is the last index of row r: 1 for the first row, 2 for the second and 0 for the third. So the loop adds 2, 5 and 6. Because every row is asked for its own length, the loop never steps off the end of a short row.

for-each over the rows#

The for-each loop from one-dimensional arrays works here too, and it shows the array of arrays plainly. Each element of grid is a row, so the outer loop variable has the type int[]:

ForEachRows.java
1public class Main {
2 public static void main(String[] args) {
3 int[][] grid = {
4 {1, 2, 3},
5 {4, 5, 6}
6 };
7
8 for (int[] row : grid) {
9 for (int value : row) {
10 System.out.print(value + " ");
11 }
12 System.out.println();
13 }
14 }
15}
Prints
›1 2 3
›4 5 6

Read for (int[] row : grid)as “for each row in the grid”. The inner loop then walks the numbers of that row. A for-each loop has no index, so use it when you only read values. When you need r and c, to change a cell or to print where something is, use the counting loops.

Building a jagged grid one row at a time#

You can also give new only the number of rows and leave the second pair of brackets empty. Then you make each row yourself:

Triangle.java
1import java.util.Arrays;
2
3public class Main {
4 public static void main(String[] args) {
5 int[][] triangle = new int[3][];
6 System.out.println(Arrays.deepToString(triangle));
7
8 for (int r = 0; r < triangle.length; r++) {
9 triangle[r] = new int[r + 1];
10 }
11 System.out.println(Arrays.deepToString(triangle));
12 }
13}
Prints
›[null, null, null]
›[[0], [0, 0], [0, 0, 0]]

new int[3][] makes the outer array with room for three rows, but no rows yet. Each slot starts as null, Java's value for “no array here”, which is what the first line prints. The loop then makes each row with its own new, one longer each time, so the second line shows rows of 1, 2 and 3 zeros. Step through it and watch the rows appear in the memory view one at a time.

When grids meet objects
Most grids you build later hold things rather than plain numbers: a board of pieces, a sheet of cells, a seating plan of people. Once you learn what an object is in why objects, you can make a 2D array of those instead of int, and the same row-and-column loops work unchanged.

A complete example: row, column and diagonal sums#

Here is a program that pulls the lesson together. It walks a square grid and adds up each row, each column, and both diagonals. Read it with the grid in front of you, then press Run.

GridSums.java
1public class Main {
2 public static void main(String[] args) {
3 // A square grid: 3 rows and 3 columns.
4 int[][] grid = {
5 {1, 2, 3},
6 {4, 5, 6},
7 {7, 8, 9}
8 };
9 int rows = grid.length;
10 int cols = grid[0].length;
11
12 // Row sums: the outer loop picks a row, the inner loop walks across it.
13 for (int r = 0; r < rows; r++) {
14 int rowSum = 0;
15 for (int c = 0; c < cols; c++) {
16 rowSum = rowSum + grid[r][c];
17 }
18 System.out.println("Row " + r + " sum: " + rowSum);
19 }
20
21 // Column sums: the outer loop picks a column, the inner loop walks down it.
22 for (int c = 0; c < cols; c++) {
23 int colSum = 0;
24 for (int r = 0; r < rows; r++) {
25 colSum = colSum + grid[r][c];
26 }
27 System.out.println("Column " + c + " sum: " + colSum);
28 }
29
30 // Both diagonals in one loop.
31 int mainDiag = 0;
32 int antiDiag = 0;
33 for (int i = 0; i < rows; i++) {
34 mainDiag = mainDiag + grid[i][i];
35 antiDiag = antiDiag + grid[i][rows - 1 - i];
36 }
37 System.out.println("Main diagonal sum: " + mainDiag);
38 System.out.println("Anti diagonal sum: " + antiDiag);
39 }
40}
Prints
›Row 0 sum: 6
›Row 1 sum: 15
›Row 2 sum: 24
›Column 0 sum: 12
›Column 1 sum: 15
›Column 2 sum: 18
›Main diagonal sum: 15
›Anti diagonal sum: 15

The program does three jobs, and most grid problems turn out to be a mix of these three loops. Here is each one in turn.

Row sums: stand on a row, walk across it

The outer loop picks a row with r. The first thing it does inside is make a fresh rowSum of 0. That reset matters: every row needs its own total, so the counter starts again each time the loop steps onto a new row. Then the inner loop slides c across the columns, adding grid[r][c] as it goes. When the inner loop runs out of columns, rowSumholds that row's total, so the program prints it and moves down to the next row.

Column sums: the same idea, turned on its side

A column is a row read from top to bottom instead of left to right. So the adding stays the same, and the two loops swap places. Now c (the column) stays still on the outside while r (the row) walks down it, adding grid[r][c]. The cells and the additions are the same; only the order of the visits changes.

Swap the loops in the printing program from earlier, and you print the grid turned on its side. Decide what this prints before you run it.

Predict the outputSideways.java
1public class Main {
2 public static void main(String[] args) {
3 int[][] grid = {
4 {1, 2, 3},
5 {4, 5, 6}
6 };
7
8 for (int c = 0; c < grid[0].length; c++) {
9 for (int r = 0; r < grid.length; r++) {
10 System.out.print(grid[r][c] + " ");
11 }
12 System.out.println();
13 }
14 }
15}

The outer loop now walks the columns. What does the program print?

It prints 1 4, then 2 5, then 3 6. The outer loop holds a column still, and the inner loop walks down the two rows of that column, so each printed line is one column of the grid. Choose Column by column in the grid widget below to watch this order.

The two diagonals: let the indexes do the walking

A diagonal is the line of cells running from one corner to the opposite corner. There are two of them. The trick is to watch how the row number and the column number relate to each other as you step down the grid. Press Next to step i from 0 to 2 and mark both diagonals on the 3 by 3 grid:

A grid, cell by cellint[][] grid
col 0col 1col 2row 0
1
2
3
row 1
4
5
6
row 2
7
8
9

Main diagonal: the row and column are the same, grid[i][i]. Anti-diagonal: the column counts back from the right, grid[i][2 - i].

Press Next to step i from 0 to 2.

On the main diagonal (top left down to bottom right) the row and the column are always the same number: grid[0][0], then grid[1][1], then grid[2][2]. So a single grid[i][i] lands on all of them, which is why one loop is enough.

The anti-diagonal (top right down to bottom left) is the mirror image. As the row i goes 0, 1, 2 down the grid, the column has to go 2, 1, 0 backwards from the right edge. The last column index is rows - 1 (here 2), so the column for each step is rows - 1 - i: when i is 0 the column is 2, when i is 1 it is 1, and when i is 2 it is 0. The rows - 1 - i counts backwards while i counts forwards.

Key idea

Fixing the row on a diagonal fixes the column too, so each diagonal needs only one loop. Main diagonal: grid[i][i]. Anti-diagonal: grid[i][rows - 1 - i]. Both only make sense on a square grid, where the number of rows equals the number of columns.

ExerciseScores.java
Three friends played three rounds of a game. Each row of scores is one player and each column is one round. Print each player's total, then each round's total, in the formats shown in the comments.

Back to one dimension: the problem set#

A grid reuses every single-row skill, so the one-dimensional moves are worth drilling until they are automatic. The end of one-dimensional arrays has ten problems, plus a bonus, solved with nothing but arrays and for loops. Some only read the array and report what they find: leaders, the longest increasing streak, frequency counts, the first repeating element, the mountain check and the balance point. Others build or rearrange an array: circular neighbor sums, the mirror difference, the wave and alternating signs.

A good order is to start with the ones that only read, then move to the ones that build. For each problem, decide what it prints before you run it, then step through it to check. When your predictions keep matching, the single-loop pattern is automatic, and a grid is that same pattern twice.

Try it

Answer each one in your head or on paper first, then open the answer.

1
For the grid {{1, 2, 3}, {4, 5, 6}}, what are grid[0][1] and grid[1][0]?
2
For that same grid, what does grid.length give, and what does grid[0].length give?
3
In the nested loop that sums the grid, why is the inner loop's bound written as grid[r].length instead of a fixed 3? Run this version, which hard-codes c < 3 against a jagged grid, and watch it crash.
4
How many times does the body total = total + grid[r][c] run for a grid with 2 rows and 3 columns? Run this and count the visit lines it prints.
5
In the print-as-table program, the empty System.out.println() sits inside the outer loop but outside the inner loop. What changes if you move it inside the inner loop? This version has it moved in. Run it and see.

Practice

Work through these on your own. Each one builds on this page.

1
On paper, declare a 2D array seats with 3 rows and 4 columns, filled with any numbers you like. Then write the nested loop that prints it as a table, and trace the exact output line by line. Check that your braces and your two .length bounds match the grid you drew.
2
Write the nested loop that finds the largest value in a 2D grid, and predict what it prints for {{4, 9, 1}, {7, 2, 8}}. Start max at grid[0][0], then compare every cell against it.
3
Build a jagged 2D array with rows of length 1, 2 and 3. Write the loop that prints, for each row, the row number and its column count. Predict the three lines it prints, then explain in one sentence why a fixed inner bound would be unsafe here.
4
Build the first six rows of Pascal's triangle in a jagged array. Row r has r + 1 numbers, the first and last number of every row is 1, and every other number is the sum of the two above it: triangle[r - 1][c - 1] + triangle[r - 1][c]. Print it one row per line. The last line should be 1 5 10 10 5 1.
5
Pick four of the ten problems at the end of one-dimensional arrays. For each one, write down what you think it prints before you run it, then run it and compare. Note any result that surprised you, and why.

Warm-up questions, with answers#

A set of short coding questions to finish on, half about one-dimensional arrays and half about grids. Each one is small on purpose. Write the loop in your head or on paper first, then open the answer to check both the code and the reasoning. Every program runs, so you can press Full screen and step through it.

One-dimensional arrays

Try it

Answer each one in your head or on paper first, then open the answer.

1
Add up every number in int[] nums = {5, 8, 2, 10} and print the total.
2
Find and print the largest value in int[] nums = {3, 9, 4, 1, 7}.
3
Count how many numbers in int[] nums = {4, 7, 10, 3, 6} are even.
4
Print the array int[] nums = {1, 2, 3, 4} backwards, one number per line.

Two-dimensional arrays

Try it

Answer each one in your head or on paper first, then open the answer.

1
Add up every cell of the grid {{1, 2}, {3, 4}} and print the total.
2
Find the largest value anywhere in the grid {{4, 9}, {7, 2}}.
3
Sum just column 1 (the middle column) of {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}.
4
Count how many cells in {{5, 1}, {8, 3}} are greater than 4.

Further reading#

Spotted a mistake, or a part that did not make sense? Report it by email