SEN1001 · Week 9 · Multidimensional Arrays

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.

two-dimensional arraymultidimensional arrayrowcolumnrow-major ordernested looparray of arraysragged arraymain diagonaltransposeArrays.deepToString

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:

  1. m[r][c]: first the row, then the column.
  2. m.length is the number of rows, m[r].length is the number of columns of that row.
  3. Outer loop is the row, inner loop is the column (row by row, row-major).
  4. A 2D array is an array of arrays. m[r] is a 1D array in its own right.
  5. 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] = 20 directly changes m[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 jag array was written as c < 3, the first (single-element) row gave ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1. When it was written as c < jag[0].length it did not crash, but only the first element of each row was added up: 8 (the correct answer is 21). Use m[r].length for 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 ideaThe truth
In m[r][c] the column comes firstThe row comes first
m.length is the total number of cellsThe number of rows; cells = rows × columns
The number of columns is m.lengthm[r].length (or m[0].length in a rectangular array)
m[2,1]m[2][1]
println inside the inner loopAfter the inner loop, inside the outer loop
Two separate array traversals for a row sumA single nested loop, sums[r] += m[r][c]
A diagonal needs a nested loopA single loop: m[i][i]
All rows must have the same lengthRagged arrays are possible
System.out.println(m) prints the tableAn 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.

Question 1easy

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
3 2 3 9

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.

Question 2easy

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
12

A new 2D int array is filled with zeros. Only two cells changed: 8 and 8 / 2 = 4. The sum is 12.

Question 3medium

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
15

The anti-diagonal: m[0][2] = 3, m[1][1] = 5, m[2][0] = 7. 3 + 5 + 7 = 15.

Question 4medium

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
14 25 36

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.

Question 5easy

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
7 5

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.

Question 6medium

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
3

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.

Question 7medium

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
10

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.

Question 8hard

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
20 3

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.

Question 9easy

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
[6, 19] 12 9

Row sums: 3 + 1 + 2 = 6, 9 + 4 + 6 = 19. Column 0: 3 + 9 = 12. The largest element is 9.

Question 10easy

How many elements does new int[3][4] contain, and what does m[2].length * m.length evaluate to?

Show answer
12 elements; 12

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.

Question 11medium

Write a static method colSum(int[][] m, int c) that returns the sum of column c of a rectangular 2D array.

Show answer
for (int r = 0; r < m.length; r++) s += m[r][c];

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.

Question 12hard

Write a static method transpose(int[][] m) that returns a new array whose rows are the columns of m (m is rectangular).

Show answer
int[][] t = new int[m[0].length][m.length]; ... t[c][r] = m[r][c];

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.

Review card

int[][] m = new int[rows][cols]; int[][] m = {{1,2,3},{4,5,6}}; m[r][c] → row first, then column m.length = number of rows m[r].length = number of columns of row r Default values are the same as 1D: 0, 0.0, false, null Traversal: for (r = 0; r < m.length; r++) for (c = 0; c < m[r].length; c++) Print row by row: println() after the inner loop Enhanced: for (int[] row : m) for (int v : row) Row sum: r fixed, c varies Column sum: c fixed, r varies Main diagonal m[i][i] anti-diagonal m[i][n-1-i] (in a square array) Transpose: t = new int[m[0].length][m.length]; t[c][r] = m[r][c]; m[r] is a 1D array (reference): int[] row = m[0]; row[1] = 5 → m[0][1] changes Ragged: int[][] t = new int[4][]; t[r] = new int[r+1]; println(m) prints an address Arrays.deepToString(m) → [[1, 2], [3, 4]] Total cells (rectangular): rows × columns