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) { // A SQUARE grid: the same number of rows as columns (3 and 3). int[][] grid = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; int rows = grid.length; // how many rows -> 3 int cols = grid[0].length; // columns in one row -> 3 // 1) ROW SUMS -> stand on one row, walk across its columns. // Outer loop 'i' chooses the row. Inner loop 'j' slides across it. for (int i = 0; i < rows; i = i + 1) { int rowSum = 0; // fresh total for THIS row for (int j = 0; j < cols; j = j + 1) { rowSum = rowSum + grid[i][j]; // add the cell (row i, col j) } System.out.println("Row " + i + " sum: " + rowSum); } // 2) COLUMN SUMS -> the row code flipped on its side. // Now the OUTER loop 'j' holds a column still, and the inner // loop 'i' walks DOWN the rows of that one column. for (int j = 0; j < cols; j = j + 1) { int colSum = 0; // fresh total for THIS column for (int i = 0; i < rows; i = i + 1) { colSum = colSum + grid[i][j]; // same cell, visited top-to-bottom } System.out.println("Column " + j + " sum: " + colSum); } // 3) THE TWO DIAGONALS -> one single loop covers both of them. int mainDiag = 0; // top-left to bottom-right, the '\' line int antiDiag = 0; // top-right to bottom-left, the '/' line for (int i = 0; i < rows; i = i + 1) { // Main diagonal: row and column are the SAME number. // i = 0 -> grid[0][0], i = 1 -> grid[1][1], i = 2 -> grid[2][2] mainDiag = mainDiag + grid[i][i]; // Anti diagonal: as the row goes down, the column steps BACK // from the right edge. Last column index is (rows - 1), so the // column we want is (rows - 1 - i). // i = 0 -> grid[0][2], i = 1 -> grid[1][1], i = 2 -> grid[2][0] 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 little jobs, three small loops. Let us walk through each one slowly, because once these three click, most grid problems turn out to be a remix of them.
The outer loop picks a row with i. The very first thing we do inside it is make a freshrowSum of 0. That reset matters: every row needs its own total, so we wipe the counter clean each time we step onto a new row. Then the inner loop slides j across the columns, adding grid[i][j] as it goes. When the inner loop runs out of columns, rowSumholds that row's whole total, so we print it and move down to the next row.
A column is just a row read top-to-bottom instead of left-to-right. So we keep the exact same adding, but swap which loop is on the outside. Now j (the column) stays still on the outside while i (the row) walks down it, adding grid[i][j]. Same cells, same additions, only the order we visit them in has changed. If you understood row sums, you already understand this one, flipped.
A diagonal is the line of cells running corner to 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. Here they are, marked on our 3×3 grid:
// The two diagonals of the 3x3 grid, marked on each cell://// col 0 col 1 col 2// row 0: 1 * 2 3 # <- grid[0][0] main, grid[0][2] anti// row 1: 4 5 *# 6 <- grid[1][1] sits on BOTH lines// row 2: 7 # 8 9 * <- grid[2][0] anti, grid[2][2] main//// * main diagonal: row == column -> grid[i][i]// # anti diagonal: column counts back -> grid[i][rows - 1 - i]col 0 col 1 col 2 row 0: 1 * 2 3 # row 1: 4 5 *# 6 row 2: 7 # 8 9 * * main diagonal: 1 + 5 + 9 = 15 # anti diagonal: 3 + 5 + 7 = 15
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 rowi goes 0, 1, 2 down the grid, the column has to go 2, 1, 0backwards from the right edge. The last column index is rows - 1 (here 2), so the column we want each step is rows - 1 - i: when i is 0 the column is2, when i is 1 it is 1, when i is 2 it is 0. That is the whole reason for that little rows - 1 - i, it just counts backwards while i counts forwards.
Fixing the row on a diagonal automatically fixes the column too, so both diagonals need only one loop. Main diagonal:
grid[i][i]. Anti-diagonal:grid[i][rows - 1 - i]. They only make sense on 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.A gentle set of short coding questions to finish on, half about one-dimensional arrays and half about grids. Every one is small on purpose. Try to write the loop in your head (or on paper) first, then tap Show answer to check both the code and a plain-English explanation of why it works. The 1D ones you can press Full screen and run for real; the 2D ones the interpreter cannot run yet, so trace those by hand against the answer.
Try each one yourself 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.Try each one yourself 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.