Arrays, two dimensions, and practice
Grids and tables, nested loops over rows and columns, and the ten one-dimensional problems.
Finished reading?
Mark this session so you can track where you are.
Grids and tables, nested loops over rows and columns, and the ten one-dimensional problems.
Finished reading?
Mark this session so you can track where you are.
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 the world is full of things that are naturally 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.
grid[r][c].grid.length and how many columns a row has with grid[r].length..length, and walking an array with a for loop feel solid, you are ready. We add exactly one new idea: putting one array inside another. No objects yet.Before we stack a second dimension on top, let us make sure 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 = i + 1) { total = total + row[i]; } System.out.println("Sum is " + total); }}Sum is 60
That loop ran three times, with i taking the values 0, 1, 2, and it printed Sum is 60. Hold onto the shape of that loop. The whole of today is two of these loops, one inside the other.
Suppose we want to store a small multiplication-style table, two rows of three numbers each:
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]); }}Top left is 1 Bottom right is 6
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, not one. The first picks the row, the second picks the column inside that row, and both still 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. Lay the grid out and label it:
// grid[row][col], both counting from 0 // col 0 col 1 col 2// row 0: 1 2 3 -> grid[0][1] is 2// row 1: 4 5 6 -> grid[1][0] is 4 // grid[0][2] is 3 (row 0, column 2)// grid[1][1] is 5 (row 1, column 1)col 0 col 1 col 2 row 0: 1 2 3 row 1: 4 5 6
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.
grid[column][row] out of habit. In our grid,grid[2][0] does not exist, there is no row 2, so it would crash with an array index out of bounds error. Always say it to yourself the same way: “row, then column”.With a 1D array, row.length told you how many boxes it had. A grid has 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.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); }}Rows: 2 Columns in row 0: 3 Columns in row 1: 3
Notice that 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 must first step into a row.
grid[r].lengthinstead of one fixed number. The reason is that Java does not actually promise every row has the same length. Most of the time they do, and a neat rectangle is what you will build. But because each row is its own array, the language lets them differ, so the honest way to ask for a row's width is to ask that row. We meet the uneven case at the end of this session.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, theinner loop walks every column of that row. The inner loop finishes completely before the outer loop moves to the next row.
r = 0 (the first row).c = 0, then 1, then 2, visiting every cell in row 0.r = 1 (the second row).c = 0, 1, 2.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. The interpreter cannot run a 2D array, so this is a read-along block; trace it on paper alongside the output.
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 = r + 1) { for (int c = 0; c < grid[r].length; c = c + 1) { total = total + grid[r][c]; } } System.out.println("The grid sums to " + total); }}The grid sums to 21
Trace the visits in order and you add 1 + 2 + 3 from the first row, giving 6, then4 + 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 we are currently on).
r for row and c for column, rather than i and j, makesgrid[r][c] read like English: “the cell at row r, column c”. Withi and j it is far too easy to flip them by mistake and read the wrong cell.A useful trick: inside the inner loop, print each value with a space after it; then after the inner loop finishes one row, println with nothing to drop to the next line. The inner loop draws a row 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 = r + 1) { for (int c = 0; c < grid[r].length; c = c + 1) { System.out.print(grid[r][c] + " "); } System.out.println(); } }}1 2 3 4 5 6
Watch where each statement sits. The 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.
We have been calling it a grid, and as a mental picture that is excellent. But it pays to know what Java is really doing underneath, because it explains everything we have seen. A 2D array is not a special grid type at all. It is simply an array whose elements are themselves arrays.
So grid is an array of length 2. Each of its two elements is a whole 1D array ofint. grid[0] is the first inner array, {1, 2, 3}. grid[1] is the second, {4, 5, 6}. When you then 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.
grid[r][c]is two lookups, not one.grid[r]selects a row, which is itself an array, and then[c]selects an element inside that row.
This single fact explains the two .length rules cleanly. 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 just the ordinary length of an ordinary array that happens to live inside another one.
And because each row is its own independent array, nothing forces them 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 = r + 1) { System.out.println("Row " + r + " has " + jagged[r].length + " columns"); } }}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. But each row reports a different width: 2, then 3, then 1. This is exactly why a safe loop always asks grid[r].length rather than assuming a fixed number. If you had hard-codedc < 3 for the inner bound, you would crash on the last row, which has only one column.
int, and the same row-and-column loops work unchanged. Nothing here is wasted.Here is a program that pulls together everything in this session: it walks a square grid and adds up each row, each column, and both diagonals. The interpreter in this course does not run two dimensional arrays, so read this one as a worked example rather than pressing Run. Trace it slowly with the grid in front of you; the loop shapes are the whole lesson.
public class Main { public static void main(String[] args) { int[][] grid = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; int rows = grid.length; int cols = grid[0].length; // Row sums: fix a row, sweep its columns. for (int i = 0; i < rows; i = i + 1) { int rowSum = 0; for (int j = 0; j < cols; j = j + 1) { rowSum = rowSum + grid[i][j]; } System.out.println("Row " + i + " sum: " + rowSum); } // Column sums: fix a column, sweep its rows. for (int j = 0; j < cols; j = j + 1) { int colSum = 0; for (int i = 0; i < rows; i = i + 1) { colSum = colSum + grid[i][j]; } System.out.println("Column " + j + " sum: " + colSum); } // Diagonals: where the two indices meet, and where they mirror. int mainDiag = 0; int antiDiag = 0; for (int i = 0; i < rows; i = i + 1) { 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); }}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
Three patterns, each only a few lines. For row sums you hold the row still and sweep the columns. For column sums you do the opposite, holding the column still and sweeping the rows. The diagonals are the elegant bit: the main diagonal is every cell where the row and column index are equal (grid[i][i]), and the anti-diagonal mirrors it from the other side (grid[i][rows - 1 - i]). Notice the diagonals only make sense for a square grid, where the number of rows equals the number of columns.
Two dimensions are a natural extension, but the real fluency comes from the one-dimensional work. Reversing an array, finding the largest value, counting how many items pass a test, searching for a value, shifting elements along: these are the moves you will reach for constantly, and they all live in a single loop with an index.
We have set up ten one-dimensional array problems in the live playground, reachable at /playground. They run for real, so you can step through each one and watch the array change. Sum, max and min, reverse, count-evens, linear search, and the rest are the best possible drill before objects arrive. Work through them until the single-loop pattern feels automatic; the nested-loop grid work today is just that same instinct, doubled.
A good order: start with the ones that only read the array (sum, max, search), then the ones that change it in place (reverse, shift). Trace each on paper first, predict the output, then run it in the playground to check yourself. When your prediction matches the trace every time, you have it.
Try each one yourself 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 and not just 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 in total for a grid with 2 rows and 3 columns? Run this and count the lines it prints.System.out.println() sits inside the outer loop but outside the inner loop. What would change if you moved it inside the inner loop instead? This version has it moved in — run it and see.Take these away. They continue exactly what we just did.
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 hand-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./playground and work through at least four of the ten one-dimensional array problems. For each, predict the output before running, then run it and confirm. Write down any one whose result surprised you and why.