AP Computer Science A

Lesson 11: Two-Dimensional ArraysAP

🎧 Listen to this lesson

Unit 8 lifts everything from Units 6 and 7 into two dimensions. A 2D array is an array of arrays: int[][] grid = new int[3][4]; creates 3 rows, each an independent int[4]. That model explains everything about the topic — why grid.length counts rows, why grid[r].length counts the columns *of that row*, why nested loops do the traversal, and why rows can have different lengths. The exam's 2D-array FRQ is traditionally the highest-scoring question for students who trace methodically and the lowest for students who rush.

Dimensions and access

int[][] grid = new int[3][4];
grid[1][2] = 7;

grid[1][2] means: take row 1 (the second array), then element 2 within it. Reading the declaration: new int[R][C] gives R rows and C columns, R·C elements in total. Two different "lengths" are in play, and confusing them is the unit's signature error:

ExpressionValue for new int[3][4]Meaning
grid.length3number of rows
grid[0].length4number of columns in row 0
grid[2][3]an intlast row, last column

Traversal: row-major order

The standard traversal visits every element row by row, using the inner bound grid[r].length rather than a hard-coded column count:

for (int r = 0; r < grid.length; r++)
{
    for (int c = 0; c < grid[r].length; c++)
    {
        // visit grid[r][c]
    }
}

Using grid[r].length is not style — it is correctness. Because each row is its own array, rows may have different lengths (a ragged array). Code that assumes grid[0].length for every row either crashes or silently skips cells in ragged data, and the exam does use ragged arrays in MCQs. Column-major traversal swaps the loops (columns outer, rows inner) when an algorithm works down a column — for example, summing one column: fix c, loop r.

The standard 2D algorithms

  • Sum everything / sum a row / sum a column. One accumulator; the loop bounds decide which.
  • Find the maximum (and its position). Track max, maxRow, maxCol together, initialised from grid[0][0].
  • Count with a condition. Identical in shape to the 1D counter.
  • Search. Nested loops with an early exit once found — remember which indices to report.
  • Tests on structure. Is every row the same length? Does any row contain a value? These are nested-loop compositions of the 1D patterns.

Trace a small computation completely before generalising. For grid = {{2, 4}, {6, 1}, {3, 5}}, summing each row:

Row rElements visited (in order)Row sumRunning total
02, 466
16, 1713
23, 5821

Real-world design: grids are everywhere

Images are 2D arrays of pixels (the old AP Picture Lab was built on this), game boards are 2D arrays of cells, spreadsheets are 2D arrays of values, and a seating chart is a 2D array of names. The traversal patterns in this lesson — visit everything, aggregate a row or column, find an extreme with its coordinates — are precisely the operations those applications perform: brighten every pixel, total a student's row of grades, find the hottest point in a sensor grid.

Key idea. Key idea: a 2D array is an array whose elements are arrays. Outer index = row, inner index = column, inner bound = grid[r].length, and every algorithm is a nested loop with a clear job for each level.

Common mistakes (from examiner reports)

  • Swapping rows and columns. new int[3][4] is 3 rows × 4 columns; grid[3] does not exist. Anchor on a drawing before coding.
  • Using grid.length for both bounds. The inner bound is grid[r].length; using the row count as the column bound works only for square arrays — and fails silently otherwise.
  • Initialising a max search to 0 rather than grid[0][0] (all-negative grids break it, as always).
  • Off-by-one in the inner loop that skips the last column or throws on it.
  • Printing the array reference (System.out.println(grid)) expecting contents — you get a type-and-address string. Contents require loops.
  • Assuming rows share length in ragged data. Always grid[r].length.
Try it. Try it: for int[][] m = {{1, 2, 3}, {4, 5}, {6}}, what are m.length, m[1].length and m[2][0]? (3, 2, and 6 — a ragged array in the wild.)

AP Exam Experience

Question 1. Consider the following code segment.

int[][] table = new int[4][5];
System.out.println(table.length + " " + table[0].length + " " + table[3][4]);

What is printed?

  • (A) 4 5 7
  • (B) 5 4 0
  • (C) 4 5 0
  • (D) 5 4 20
  • (E) Nothing; an exception is thrown.

Answer: (C). table.length counts the 4 rows; table[0].length counts the 5 columns in row 0; a fresh int element defaults to 0, and index [3][4] is the valid last cell. (E) tempts students who misread the last valid index as out of bounds.

Question 2. Consider the following code segment, where grid is a 2D array of int values.

int total = 0;
for (int c = 0; c < grid[0].length; c++)
{
    for (int r = 0; r < grid.length; r++)
    {
        total += grid[r][c];
    }
}

Which of the following best describes what the segment computes?

  • (A) The sum of the elements in the first row of grid
  • (B) The sum of all elements of grid
  • (C) The sum of the elements in the first column of grid
  • (D) The sum of all elements except those in the last row
  • (E) The sum of all elements except those in the last column

Answer: (B). The loops are swapped relative to the usual order, but every (r, c) pair is still visited exactly once — order of accumulation does not change a sum. Reading loop *structure* rather than loop *order* is the skill being tested; (A) and (C) describe a single loop, not nested loops.

Question 3. Consider the following 2D array and code segment.

int[][] g = {{5, 1}, {2, 8}, {4, 4}};
int best = g[0][0];
int bestRow = 0;
for (int r = 0; r < g.length; r++)
{
    for (int c = 0; c < g[r].length; c++)
    {
        if (g[r][c] > best)
        {
            best = g[r][c];
            bestRow = r;
        }
    }
}
System.out.println(best + " " + bestRow);

What is printed?

  • (A) 8 1
  • (B) 8 2
  • (C) 5 0
  • (D) 4 2
  • (E) 8 0

Answer: (A). The maximum value in the grid is 8, located at row 1, column 1. The trace matters: best updates to 5 (start), stays through row 0, then becomes 8 at (1, 1), recording row 1; the 4s in row 2 never beat it.

Free-Response Question. This question involves analysing a rectangular 2D array of int values representing daily rainfall measurements, with at least one row and one column. (a) Write the static method rowWithGreatestSum, which takes an int[][] parameter and returns the index of the row whose elements have the greatest sum. If two or more rows tie for the greatest sum, return the smallest such index. (b) Write the static method countAbove, which takes an int[][] parameter and an int threshold, and returns the number of elements in the entire array that are strictly greater than the threshold.

Scoring guideline (9 points). Part (a) — 6 points:

  • +1 Correct method header: static, returns int, one int[][] parameter
  • +1 Computes the sum of row 0 (or otherwise initialises the best sum from actual data, not 0)
  • +1 Iterates over all remaining rows and computes each row's full sum
  • +1 Compares each row sum to the best sum using strict > (so the earliest row wins ties)
  • +1 Tracks the index of the best row alongside its sum
  • +1 Returns the index of the row with the greatest sum

Part (b) — 3 points:

  • +1 Correct method header: static, returns int, parameters int[][] and int
  • +1 Traverses every element using grid[r].length as the inner bound
  • +1 Counts and returns the elements strictly greater than the threshold

Model solution.

public static int rowWithGreatestSum(int[][] grid)
{
    int bestRow = 0;
    int bestSum = 0;
    for (int c = 0; c < grid[0].length; c++)
    {
        bestSum += grid[0][c];
    }
    for (int r = 1; r < grid.length; r++)
    {
        int sum = 0;
        for (int c = 0; c < grid[r].length; c++)
        {
            sum += grid[r][c];
        }
        if (sum > bestSum)
        {
            bestSum = sum;
            bestRow = r;
        }
    }
    return bestRow;
}

public static int countAbove(int[][] grid, int threshold)
{
    int count = 0;
    for (int r = 0; r < grid.length; r++)
    {
        for (int c = 0; c < grid[r].length; c++)
        {
            if (grid[r][c] > threshold)
            {
                count++;
            }
        }
    }
    return count;
}


## Going deeper: diagonal sums and the shape test
Two more standard 2D moves round out the toolkit. First, checking structure: a grid is **square** when every row's length equals the number of rows — `grid[r].length == grid.length` for all `r`; assuming it instead of checking it is how ragged arrays ambush solutions. Second, summing a **diagonal** of a square grid replaces the nested loop with a single loop, because the coordinates march together (`grid[i][i]`):

int main = 0; for (int i = 0; i < grid.length; i++) { main += grid[i][i]; }

For the grid {{2, 4, 1}, {6, 1, 3}, {3, 5, 8}} the main diagonal sums 2 + 1 + 8 = 11, and the anti-diagonal (`grid[i][grid.length - 1 - i]`) sums 1 + 1 + 3 = 5. Recognising when a 2D problem collapses to a 1D walk — diagonals, borders, a single row or column — saves real minutes on the exam, and the tell is always that the two indices are linked by a formula instead of varying independently.

**Bonus question.** For the grid above, what does the double loop `for r, for c: if (r == c) count++` leave in `count` — and what if the test were `r + c == 2`?
- (A) 3 and 3
- (B) 9 and 3
- (C) 3 and 9
- (D) 2 and 2
- (E) 3 and 2

**Answer: (A).** The main diagonal of a 3×3 grid has exactly 3 cells, and the anti-diagonal condition `r + c == 2` also picks exactly 3 cells ((0,2), (1,1), (2,0)) — coordinate-formula tests select lines through the grid, not regions.

📊 Interactive

Interactive — hover or explore the chart above.

Keep reading to complete this lesson