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 BasithUpdated 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.
- 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 withgrid[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.
.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.
public class Main { public static void main(String[] args) { int[] row = {10, 20, 30}; int total = 0; for (int i = 0; i < row.length; i++) { total = total + row[i]; } System.out.println("Sum is " + total); }}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:
public class Main { public static void main(String[] args) { int[][] grid = { {1, 2, 3}, {4, 5, 6} }; System.out.println("Top left is " + grid[0][0]); System.out.println("Bottom right is " + grid[1][2]); grid[0][1] = 20; System.out.println("Row 0, column 1 is now " + grid[0][1]); }}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.
Tap any cell to see the name Java uses for it.
Key ideaA 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.
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”.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.lengthis the number of rows. It asks the outer array how many inner arrays it holds.grid[r].lengthis the number of columns in rowr. It asks one particular row how many numbers it holds.
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("Columns in row 0: " + grid[0].length); System.out.println("Columns in row 1: " + grid[1].length); }}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.
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.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.
- 1The outer loop sets
r = 0(the first row). - 2The inner loop runs fully:
c = 0, then1, then2, visiting every cell in row 0. - 3The outer loop moves on:
r = 1(the second row). - 4The inner loop runs fully again for row 1:
c = 0, 1, 2. - 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.
public class Main { public static void main(String[] args) { int[][] grid = { {1, 2, 3}, {4, 5, 6} }; int total = 0; for (int r = 0; r < grid.length; r++) { for (int c = 0; c < grid[r].length; c++) { total = total + grid[r][c]; } } System.out.println("The grid sums to " + total); }}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).
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.
public class Main { public static void main(String[] args) { int[][] grid = { {1, 2, 3}, {4, 5, 6} }; for (int r = 0; r < grid.length; r++) { for (int c = 0; c < grid[r].length; c++) { System.out.print(grid[r][c] + " "); } System.out.println(); } }}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.
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.
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:
import java.util.Arrays; public class Main { public static void main(String[] args) { int[][] board = new int[3][4]; System.out.println(Arrays.deepToString(board)); board[1][2] = 7; System.out.println(Arrays.deepToString(board)); System.out.println(board.length + " rows of " + board[0].length); }}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.
public class Main { public static void main(String[] args) { int[][] times = new int[4][5]; for (int r = 0; r < times.length; r++) { for (int c = 0; c < times[r].length; c++) { times[r][c] = (r + 1) * (c + 1); } } for (int r = 0; r < times.length; r++) { for (int c = 0; c < times[r].length; c++) { System.out.print(times[r][c] + "\t"); } System.out.println(); } }}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.
public class Main { public static void main(String[] args) { int[][] grid = { {1, 2, 3}, {4, 5, 6} }; int[] top = grid[0]; System.out.println("grid has " + grid.length + " rows"); System.out.println("top has " + top.length + " numbers"); System.out.println("top[2] is " + top[2]); }}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.
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:
public class Main { public static void main(String[] args) { int[][] jagged = { {1, 2}, {3, 4, 5}, {6} }; System.out.println("Rows: " + jagged.length); for (int r = 0; r < jagged.length; r++) { System.out.println("Row " + r + " has " + jagged[r].length + " 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.
public class Main { public static void main(String[] args) { int[][] jagged = { {1, 2}, {3, 4, 5}, {6} }; int total = 0; for (int r = 0; r < jagged.length; r++) { int last = jagged[r].length - 1; total = total + jagged[r][last]; } System.out.println(total); }}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[]:
public class Main { public static void main(String[] args) { int[][] grid = { {1, 2, 3}, {4, 5, 6} }; for (int[] row : grid) { for (int value : row) { System.out.print(value + " "); } System.out.println(); } }}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:
import java.util.Arrays; public class Main { public static void main(String[] args) { int[][] triangle = new int[3][]; System.out.println(Arrays.deepToString(triangle)); for (int r = 0; r < triangle.length; r++) { triangle[r] = new int[r + 1]; } System.out.println(Arrays.deepToString(triangle)); }}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.
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.
public class Main { public static void main(String[] args) { // A square grid: 3 rows and 3 columns. int[][] grid = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; int rows = grid.length; int cols = grid[0].length; // Row sums: the outer loop picks a row, the inner loop walks across it. for (int r = 0; r < rows; r++) { int rowSum = 0; for (int c = 0; c < cols; c++) { rowSum = rowSum + grid[r][c]; } System.out.println("Row " + r + " sum: " + rowSum); } // Column sums: the outer loop picks a column, the inner loop walks down it. for (int c = 0; c < cols; c++) { int colSum = 0; for (int r = 0; r < rows; r++) { colSum = colSum + grid[r][c]; } System.out.println("Column " + c + " sum: " + colSum); } // Both diagonals in one loop. int mainDiag = 0; int antiDiag = 0; for (int i = 0; i < rows; i++) { mainDiag = mainDiag + grid[i][i]; antiDiag = antiDiag + grid[i][rows - 1 - i]; } System.out.println("Main diagonal sum: " + mainDiag); System.out.println("Anti diagonal sum: " + antiDiag); }}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.
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++) { for (int r = 0; r < grid.length; r++) { System.out.print(grid[r][c] + " "); } System.out.println(); } }}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:
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 ideaFixing 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.
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, 2, 3}, {4, 5, 6}}, what are grid[0][1] and grid[1][0]?grid.length give, and what does grid[0].length give?grid[r].length instead of a fixed 3? Run this version, which hard-codes c < 3 against a jagged grid, and watch it crash.total = total + grid[r][c] run for a grid with 2 rows and 3 columns? Run this and count the visit lines it prints.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.
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.{{4, 9, 1}, {7, 2, 8}}. Start max at grid[0][0], then compare every cell against it.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.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.
int[] nums = {5, 8, 2, 10} and print the total.int[] nums = {3, 9, 4, 1, 7}.int[] nums = {4, 7, 10, 3, 6} are even.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, 2}, {3, 4}} and print the total.{{4, 9}, {7, 2}}.{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}.{{5, 1}, {8, 3}} are greater than 4.Further reading#
- Creating Arrays in Your Programs, the dev.java tutorial, including arrays of arrays and rows of different lengths
- java.util.Arrays in the Java SE 25 API documentation, with deepToString and the other array helpers
- Arrays in the Java Language Specification, chapter 10, the exact rules for creating and indexing arrays
Spotted a mistake, or a part that did not make sense? Report it by email