Two-dimensional arrays (2D arrays)
Tables made of rows and columns: creating them, m.length versus m[r].length, traversing with nested loops, row/column sums, the diagonal, transpose and ragged arrays. On the final it comes as tracing and method writing.
Some data is naturally in the shape of a table: the grade of each student in a class on each exam, a chessboard, the seats in a cinema hall, a matrix. In Java a two-dimensional array is used for this. There are few new concepts, but you have to apply the 1D array rules correctly with two indexes. Typical exam questions: “what does this nested loop print when it traverses the 2D array?” and “write a method that returns the row/column sum”.
Golden rules:
m[r][c]: first the row, then the column.m.lengthis the number of rows,m[r].lengthis the number of columns of that row.- Outer loop is the row, inner loop is the column (row by row, row-major).
- A 2D array is an array of arrays.
m[r]is a 1D array in its own right. - For the column bound, write
m[r].length, not a fixed number. It works correctly with ragged arrays too.
Creating
int[][] grid = new int[3][4]; // 3 rows, 4 columns, all 0
grid[0][1] = 5;
grid[2][3] = 9;
System.out.println(grid.length + " " + grid[0].length + " " + grid[0][1] + " " + grid[1][1]);
3 4 5 0
column 0 column 1 column 2 column 3
row 0 0 5 0 0
row 1 0 0 0 0
row 2 0 0 0 9
If you know the values from the start, use the initializer list: each inner curly-brace group is a row.
int[][] m = {
{1, 2, 3},
{4, 5, 6}
};
Traversing and printing
for (int r = 0; r < m.length; r++) {
for (int c = 0; c < m[r].length; c++) {
System.out.print(m[r][c] + " ");
}
System.out.println(); // the row is finished
}
System.out.println(m[1][0] + " " + m[0][2] + " " + m.length + " " + m[1].length);
System.out.println(Arrays.toString(m[1]));
System.out.println(Arrays.deepToString(m));
1 2 3
4 5 6
4 3 2 3
[4, 5, 6]
[[1, 2, 3], [4, 5, 6]]
m[1] is the whole second row, that is, the array {4, 5, 6}; that is why Arrays.toString(m[1]) works. To see the whole table on one line, use Arrays.deepToString.
For an aligned table use a printf width:
for (int[] row : m) {
for (int v : row) System.out.printf("%4d", v * 10);
System.out.println();
}
10 20 30
40 50 60
Enhanced for can be nested too: the type of the outer variable is int[] (a row), the type of the inner one is int (a cell).
Traversing column by column
If you swap the places of the loops, the table is read column by column. In a rectangular array, m[0].length is used for the column bound:
for (int c = 0; c < m[0].length; c++) {
for (int r = 0; r < m.length; r++) System.out.print(m[r][c] + " ");
}
1 4 2 5 3 6
Sums, maximum, diagonal
public static int total(int[][] m) {
int t = 0;
for (int[] row : m)
for (int v : row)
t += v;
return t;
}
public static int[] rowSums(int[][] m) {
int[] sums = new int[m.length]; // one sum for each row
for (int r = 0; r < m.length; r++)
for (int c = 0; c < m[r].length; c++)
sums[r] += m[r][c];
return sums;
}
public static int colSum(int[][] m, int c) {
int s = 0;
for (int r = 0; r < m.length; r++) s += m[r][c];
return s;
}
public static int max(int[][] m) {
int best = m[0][0];
for (int r = 0; r < m.length; r++)
for (int c = 0; c < m[r].length; c++)
if (m[r][c] > best) best = m[r][c];
return best;
}
// main: m = {{1, 2, 3}, {4, 5, 6}}
System.out.println(total(m) + " " + Arrays.toString(rowSums(m)) + " " + colSum(m, 1) + " " + max(m));
21 [6, 15] 7 6
The trace table of rowSums, for m = {{1, 2, 3}, {4, 5, 6}}:
r c m[r][c] sums
0 0 1 [1, 0]
0 1 2 [3, 0]
0 2 3 [6, 0]
1 0 4 [6, 4]
1 1 5 [6, 9]
1 2 6 [6, 15]
In a row sum the row is fixed and the column varies. In a column sum the column is fixed and the row varies. Thinking about which one stays fixed is the short way to set up the loop correctly.
Diagonals (in a square array)
int[][] sq = {
{2, 0, 1},
{3, 5, 4},
{9, 8, 6}
};
int diag = 0;
for (int i = 0; i < sq.length; i++) diag += sq[i][i]; // main diagonal
int anti = 0;
for (int i = 0; i < sq.length; i++) anti += sq[i][sq.length - 1 - i]; // anti-diagonal
13
15
On the main diagonal the row and column indexes are equal: 2 + 5 + 6. On the anti-diagonal they add up to n - 1: sq[0][2] + sq[1][1] + sq[2][0] = 1 + 5 + 9. For a diagonal a single loop is enough.
Transpose
Turning rows into columns: the dimensions of the new array are swapped, t[c][r] = m[r][c]. For {{1, 2, 3}, {4, 5, 6}}:
[[1, 4], [2, 5], [3, 6]]
Filling with a formula
If every cell is computed from the indexes, like a multiplication table:
int[][] table = new int[3][3];
for (int r = 0; r < 3; r++)
for (int c = 0; c < 3; c++)
table[r][c] = (r + 1) * (c + 1);
System.out.println(Arrays.deepToString(table));
[[1, 2, 3], [2, 4, 6], [3, 6, 9]]
Arrays of arrays and ragged arrays
int[][] m is really an array each of whose elements is an int[]. m itself holds the references of the rows. That is why:
int[] row = m[0];takes the reference of the first row;row[1] = 20directly changesm[0][1].- The lengths of the rows can differ: a ragged array (ragged / jagged array).
int[][] tri = new int[4][]; // 4 rows, no columns yet
for (int r = 0; r < tri.length; r++) {
tri[r] = new int[r + 1]; // r+1 elements in row r
for (int c = 0; c <= r; c++) tri[r][c] = r + c;
}
for (int[] row : tri) System.out.println(Arrays.toString(row));
int[][] jag = {{1}, {2, 3, 4}, {5, 6}};
System.out.println(jag.length + " " + jag[1].length + " " + jag[2][1]);
[0]
[1, 2]
[2, 3, 4]
[3, 4, 5, 6]
3 3 6
PITFALL · writing the number of columns as a constant. When the inner loop on the
jagarray was written asc < 3, the first (single-element) row gaveArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1. When it was written asc < jag[0].lengthit did not crash, but only the first element of each row was added up: 8 (the correct answer is 21). Usem[r].lengthfor each row.
Passing a 2D array to a method
The same rule as 1D: a copy of the reference goes, and if the method changes the cells, the caller sees it too. The parameter type is int[][], and the return type can also be int[][] (as in transpose). You do not need to pass the number of rows and columns as separate parameters; you find them out inside the method with length.
Common mistakes
| Wrong idea | The truth |
|---|---|
In m[r][c] the column comes first | The row comes first |
m.length is the total number of cells | The number of rows; cells = rows × columns |
The number of columns is m.length | m[r].length (or m[0].length in a rectangular array) |
m[2,1] | m[2][1] |
| println inside the inner loop | After the inner loop, inside the outer loop |
| Two separate array traversals for a row sum | A single nested loop, sums[r] += m[r][c] |
| A diagonal needs a nested loop | A single loop: m[i][i] |
| All rows must have the same length | Ragged arrays are possible |
System.out.println(m) prints the table | An address; use Arrays.deepToString(m) |
In transpose the new array is new int[m.length][m[0].length] | The dimensions are swapped: [m[0].length][m.length] |
Test yourself
Exam-style questions. Solve them on paper first, then reveal the answer.
What is printed?
int[][] m = {{4, 7}, {1, 9}, {3, 2}};
System.out.println(m.length + " " + m[0].length + " " + m[2][0] + " " + m[1][1]);Show answer
Three inner curly-brace groups = 3 rows, each with 2 elements = 2 columns. m[2][0]: row 2 is {3, 2}, column 0 → 3. m[1][1]: element 1 of {1, 9} → 9.
What is printed?
int[][] m = new int[2][3];
m[1][2] = 8;
m[0][0] = m[1][2] / 2;
int s = 0;
for (int[] row : m) for (int v : row) s += v;
System.out.println(s);Show answer
A new 2D int array is filled with zeros. Only two cells changed: 8 and 8 / 2 = 4. The sum is 12.
What is printed?
int[][] m = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int s = 0;
for (int r = 0; r < 3; r++) s += m[r][2 - r];
System.out.println(s);Show answer
The anti-diagonal: m[0][2] = 3, m[1][1] = 5, m[2][0] = 7. 3 + 5 + 7 = 15.
What is printed?
int[][] m = {{1, 2, 3}, {4, 5, 6}};
for (int c = 0; c < 3; c++) {
for (int r = 0; r < 2; r++) System.out.print(m[r][c]);
System.out.print(" ");
}Show answer
The outer loop is the column, the inner loop is the row: the array is read column by column. Column 0: 1, 4. Column 1: 2, 5. Column 2: 3, 6. A space after each column.
What is printed?
int[][] m = new int[3][3];
for (int r = 0; r < 3; r++)
for (int c = 0; c < 3; c++)
m[r][c] = r * 3 + c;
System.out.println(m[2][1] + " " + m[1][2]);Show answer
The formula numbers the cells from 0 to 8, row by row. m[2][1] = 2 * 3 + 1 = 7, m[1][2] = 1 * 3 + 2 = 5.
What is printed?
int[][] m = {{5, 1}, {2, 8}, {6, 3}};
int cnt = 0;
for (int r = 0; r < m.length; r++)
for (int c = 0; c < m[r].length; c++)
if (m[r][c] > r * 3) cnt++;
System.out.println(cnt);Show answer
The threshold changes on each row. r = 0: threshold 0, 5 and 1 pass (2). r = 1: threshold 3, only 8 (1). r = 2: threshold 6, 6 > 6 is false, and 3 does not either (0). Total 3.
What is printed?
int[][] m = {{1}, {2, 3}, {4, 5, 6}};
int s = 0;
for (int r = 0; r < m.length; r++) s += m[r][m[r].length - 1];
System.out.println(s);Show answer
A ragged array: the row lengths are 1, 2, 3. The last element of each row: 1, 3, 6. Total 10. Always use m[r].length for the number of columns, not a fixed number.
What is printed?
int[][] m = {{1, 2}, {3, 4}};
int[] row0 = m[0];
row0[1] = 20;
m[1] = new int[]{7, 7, 7};
System.out.println(m[0][1] + " " + m[1].length);Show answer
A 2D array is really an array of arrays. row0 = m[0] takes the reference of the first row; row0[1] = 20 changes m[0][1]. A new 3-element row can be put in place of m[1]; row lengths can differ.
Using rowSums (returns an array with the sum of each row), colSum(m, c) and max(m), what is printed?
int[][] m = {{3, 1, 2}, {9, 4, 6}};
System.out.println(Arrays.toString(rowSums(m)) + " " + colSum(m, 0) + " " + max(m));Show answer
Row sums: 3 + 1 + 2 = 6, 9 + 4 + 6 = 19. Column 0: 3 + 9 = 12. The largest element is 9.
How many elements does new int[3][4] contain, and what does m[2].length * m.length evaluate to?
Show answer
3 rows × 4 columns = 12 cells. m.length is the number of rows (3), m[2].length is the number of columns in that row (4). 4 * 3 = 12.
Write a static method colSum(int[][] m, int c) that returns the sum of column c of a rectangular 2D array.
Show answer
The column is fixed, the row changes: one loop is enough.public static int colSum(int[][] m, int c) { int s = 0; for (int r = 0; r < m.length; r++) { s += m[r][c]; } return s;}Test: for {{1, 2, 3}, {4, 5, 6}}, colSum(m, 1) → 7.
Write a static method transpose(int[][] m) that returns a new array whose rows are the columns of m (m is rectangular).
Show answer
The dimensions of the new array are swapped: its number of rows is the number of columns of m, its number of columns is the number of rows of m.public static int[][] transpose(int[][] m) { int[][] t = new int[m[0].length][m.length]; for (int r = 0; r < m.length; r++) for (int c = 0; c < m[0].length; c++) t[c][r] = m[r][c]; return t;}Test: {{1, 2, 3}, {4, 5, 6}} → [[1, 4], [2, 5], [3, 6]]. {{1, 2}, {3, 4}} → t[0][1] = 3, t[1][0] = 2.
Verification: All outputs were run with JDK 26 (verify/SEN1001/W09.java and W09b.java). The spaces in the printf alignment were checked with cat -A.