2D Array Traversals
Why this matters
Imagine you're in charge of the seating chart for your school's production of Hamilton. You have a 2D array representing the theater: each row is a row of seats, and each "column" in that row is a specific seat. Now, imagine the director wants to place a flyer on every single seat. How would you do it systematically?
You wouldn't just run around randomly. You'd probably start with the first row, walk across, and place a flyer on each seat. Then you'd move to the second row and do the same, and so on, until you've covered the entire theater. This methodical, row-by-row process is exactly what we call "traversal." In this lesson, we'll learn how to write code that walks through 2D arrays just like that, and we'll even see how to walk through them column by column.
Concept overview
flowchart TD
A[Start] --> B{Initialize row i = 0};
B --> C{i < num_rows?};
C -- Yes --> D{Initialize col j = 0};
C -- No --> K[End];
D --> E{j < num_cols?};
E -- Yes --> F[Process element grid[i][j]];
F --> G{j = j + 1};
G --> E;
E -- No --> H{i = i + 1};
H --> C;
Core explanation
Alright, let's dive into how we can programmatically walk through our 2D arrays.
The Big Idea: An Array of Arrays
First, let's solidify our mental model. A 2D array in Java is really an array of arrays.
Think of a parking garage. The entire garage is your 2D array. Each level of the garage is a single 1D array. And each parking spot on a level is an element.
// A 3x4 parking garage (3 levels, 4 spots per level)
<figure class="lesson-figure"><div class="shr-widget" data-shr-widget="{"type":"array_animation","label":"Parking Garage Grid","values":[101,102,103,104,201,202,203,204,301,302,303,304],"highlight":[0]}" aria-label="A 3x4 grid showing indices for rows 0-2 and columns 0-3."></div><figcaption class="lesson-figure-caption">A 2D array visualized as a grid of rows and columns.</figcaption></figure>
int[][] parkingGarage = {
{101, 102, 103, 104}, // Level 1 (a 1D array)
{201, 202, 203, 204}, // Level 2 (a 1D array)
{301, 302, 303, 304} // Level 3 (a 1D array)
};
To get to a specific spot, you need two pieces of information: the level (row) and the spot number (column). parkingGarage[1][2] would get you to spot 203 (remember, we start counting from 0!).
Row-Major Traversal: The Standard Walkthrough
Row-major traversal is the most common and intuitive way to visit every element. It's just like reading a book: you go left-to-right across the first line, then move to the second line, and so on.
To do this, we use nested for loops.
- The outer loop handles the rows. It iterates from row
0to the last row. - The inner loop handles the columns. For each row, it iterates from column
0to the last column in that row.
int[][] scores = {
{88, 92, 78},
{95, 89, 91},
{76, 81, 85},
{99, 94, 90}
};
// Row-major traversal to print all scores
<figure class="lesson-figure"><div class="shr-widget" data-shr-widget="{"code":["for (int i = 0; i < 4; i++)"," for (int j = 0; j < 3; j++)"," print(scores[i][j]);"],"type":"for_loop_trace","array":[88,92,78,95,89,91],"trace_steps":[{"line":1,"vars":{"i":0,"j":0}},{"line":2,"vars":{"i":0,"j":0},"highlight_cell":0},{"line":2,"vars":{"i":0,"j":1},"highlight_cell":1},{"line":2,"vars":{"i":0,"j":2},"highlight_cell":2},{"line":1,"vars":{"i":1,"j":0},"highlight_cell":3}]}" aria-label="Animation showing i and j indices moving through a 2D array."></div><figcaption class="lesson-figure-caption">Tracing the nested loops for row-major order.</figcaption></figure>
for (int i = 0; i < scores.length; i++) { // Outer loop for rows
for (int j = 0; j < scores[i].length; j++) { // Inner loop for columns
System.out.print(scores[i][j] + " ");
}
System.out.println(); // New line after each row
}
Let's break down those loop conditions:
scores.length: This gives you the number of rows (in this case, 4). It's the "height" of the 2D array.scores[i].length: This gives you the number of columns in the current rowi. It's the "width" of that specific row. For the AP exam, you can usually assume all rows have the same length, soscores[0].lengthwould also work.
Column-Major Traversal: Walking Down the Aisles
Sometimes, you need to process the data column by column. Imagine you wanted to find the average score for the first quiz, then the second, then the third. You'd need to go down the columns.
This is called column-major traversal. To achieve this, we flip the logic of our loops.
- The outer loop now handles the columns.
- The inner loop now handles the rows.
// Column-major traversal to print all scores
// Assume a rectangular array (all rows have same length)
int numCols = scores[0].length;
int numRows = scores.length;
for (int j = 0; j < numCols; j++) { // Outer loop for columns
for (int i = 0; i < numRows; i++) { // Inner loop for rows
// The access order is STILL [row][col] or [i][j]!
System.out.print(scores[i][j] + " ");
}
System.out.println(); // New line after each column is processed
}
The output would look different:
88 95 76 99
92 89 81 94
78 91 85 90
The most common mistake here is to try to access the element as scores[j][i]. Don't do that! The physical structure of the array is still [row][col]. We are just changing the order in which we visit the elements. Think of it this way: i is always your row index, and j is always your column index. We just put the j loop on the outside to prioritize columns.
Using the Enhanced For Loop
You can also traverse a 2D array with nested enhanced for loops. This can be cleaner, but it has two important characteristics:
- It is always row-major.
- It is "read-only"—you can't use it to change the values in the array.
Here’s the structure:
// Enhanced for loop traversal (always row-major)
for (int[] row : scores) { // Outer loop: get each 1D array (row)
for (int score : row) { // Inner loop: get each int from the row
System.out.print(score + " ");
}
System.out.println();
}
Look closely at the data types.
- The outer loop variable,
row, must be of typeint[], becausescoresis an array ofintarrays. Each time the outer loop runs,rowholds one of the inner arrays (e.g.,{88, 92, 78}). - The inner loop variable,
score, is of typeint, because it's iterating through the elements of theint[]namedrow.
A critical warning: You cannot modify the array this way. Consider this code:
for (int[] row : scores) {
for (int score : row) {
score = 0; // This does NOT change the array!
}
}
This code does not fill the scores array with zeros. The variable score is a copy of the value from the array. Changing the copy doesn't affect the original. If you need to modify the array's contents, you must use a standard indexed for loop.
See it in action
Worked examples
Let's put these concepts into practice with a few common scenarios.
Calculating the Average of All Grades
Problem: You are given a 2D array of student grades. Write a method to calculate the average of all the grades in the array.
Data:
double[][] grades = {
{85.5, 92.0, 76.8},
{99.0, 88.5, 91.2}
};
Solution Walkthrough:
- 1GoalWe need to visit every single grade, add it to a running total, and keep track of how many grades we've seen.
- 2StrategyA row-major traversal is perfect for this. We'll use nested
forloops to visit each element. We need adoublevariable for the sum and anintfor the count. - 3Implementation
public double calculateAverage(double[][] grades) { double sum = 0.0; int count = 0; for (int i = 0; i < grades.length; i++) { for (int j = 0; j < grades[i].length; j++) { sum += grades[i][j]; // Add the current grade to the sum count++; // Increment the count of grades } } // Avoid division by zero if the array is empty! if (count == 0) { return 0.0; } return sum / count; } - 4Why it worksThe outer loop (
i) iterates through the rows (0 then 1). For each row, the inner loop (j) iterates through the columns (0, 1, then 2). The expressiongrades[i][j]correctly accesses each element one by one (85.5,92.0,76.8, then99.0, etc.), adding it tosum. Finally, dividing the total sum by the total count gives us the average.
Finding the Maximum Value in Each Column
Problem: You have a 2D array representing daily sales for different stores in Dallas. Find the highest sales figure for each day (i.e., for each column).
Data:
int[][] sales = {
{1200, 1500, 1350}, // Store 1
{1100, 1800, 1400}, // Store 2
{1450, 1480, 1600} // Store 3
};
Solution Walkthrough:
- 1GoalWe need to process the data column by column. For column 0, we find the max. For column 1, we find the max, and so on.
- 2StrategyThis screams for column-major traversal. The outer loop will iterate through columns, and for each column, the inner loop will scan down the rows to find the maximum value in that column.
- 3Implementation
public void findMaxInEachColumn(int[][] data) { if (data.length == 0) return; // Handle empty array int numCols = data[0].length; int numRows = data.length; for (int j = 0; j < numCols; j++) { // Outer loop for columns int maxInCol = data[0][j]; // Initialize max with the first element of the column for (int i = 1; i < numRows; i++) { // Inner loop for rows (start at 1) if (data[i][j] > maxInCol) { maxInCol = data[i][j]; } } System.out.println("Max for column " + j + " is: " + maxInCol); } } - 4Why it worksThe outer loop (
j) selects a column. We initializemaxInColto the first element in that column (data[0][j]). Then, the inner loop (i) scans down the rest of that same column (data[1][j],data[2][j], etc.), updatingmaxInColwhenever a larger value is found. After the inner loop finishes, we have the max for that one column and print it before the outer loop moves to the next column.
Try it yourself
Ready to try a couple on your own? Don't worry about writing a full class, just focus on the method logic.
Problem 1: Find the Maximum Value
Write a method findMax that takes a 2D array of integers and returns the single largest value in the entire array.
- Hint: You'll need a variable, maybe called
currentMax, to keep track of the largest value you've seen so far. What should you initialize it to? Think about what would happen if all the numbers in the array were negative. A safe bet is to initialize it to the very first element,grid[0][0].
Problem 2: "Flatten" the Array
Write a method flatten that takes a 2D array of integers (int[][]) and returns a new 1D array (int[]) containing all the elements from the 2D array, read in row-major order.
- Hint: First, figure out the total number of elements to determine the size of the new 1D array you need to create. You'll also need a separate counter variable to keep track of the current position in your new 1D array as you fill it.
Practice — 8 questions
In simple terms, 2D array traversal is about using nested loops to visit every item in a grid of data, either row-by-row or column-by-column, to perform some task.
// A 3x4 parking garage (3 levels, 4 spots per level)
[[fig:parking_garage_2d]]
int[][] parkingGarage = {
{101, 102, 103, 104}, // Level 1 (a 1D array)
{201, 202, 203, 204}, // Level 2 (a 1D array)
{301, 302, 303, 304} // Level 3 (a 1D array)
};
- 4.12.A: Develop code used to traverse the elements in a 2D array and determine the result of these traversals.
- 4.12.A.1
- Nested iteration statements are used to traverse and access all or an ordered sequence of elements in a 2D array. Since 2D arrays are stored as arrays of arrays, the way 2D arrays are traversed using for loops and enhanced for loops is similar to 1D array objects. Nested iteration statements can be written to traverse the 2D array in row-major order, column-major order, or a uniquely defined order. Row-major order refers to an ordering of 2D array elements where traversal occurs across each row, whereas column-major order traversal occurs down each column.
- 4.12.A.2
- The outer loop of a nested enhanced for loop used to traverse a 2D array traverses the rows. Therefore, the enhanced for loop variable must be the type of each row, which is a 1D array. The inner loop traverses a single row. Therefore, the inner enhanced for loop variable must be the same type as the elements stored in the 1D array. Assigning a new value to the enhanced for loop variable does not change the value stored in the array.
flowchart TD
A[Start] --> B{Initialize row i = 0};
B --> C{i < num_rows?};
C -- Yes --> D{Initialize col j = 0};
C -- No --> K[End];
D --> E{j < num_cols?};
E -- Yes --> F[Process element grid[i][j]];
F --> G{j = j + 1};
G --> E;
E -- No --> H{i = i + 1};
H --> C;
Read what Saavi narrates
Hello everyone, it's Saavi from Shrutam. Let's talk about something we do all the time without thinking: organizing things in a grid.
Imagine you're in charge of the seating chart for your school's production of Hamilton. You have this big grid of seats. Now, if you need to put a flyer on every single seat, how would you do it? You'd probably start with the first row, walk all the way across, then move to the second row and do the same thing, right? That systematic process is exactly what we call traversal in computer science.
Today, we're learning how to write code that walks through 2D arrays, which are just grids of data. We can go row-by-row, just like with the theater seats, or even go column-by-column.
Let's look at a quick example. Imagine we have a 2D array of grades and we want to find the average. We need to visit every grade, add it to a running total, and count how many grades we've seen. A row-by-row, or row-major, traversal is perfect for this.
We'd set up a variable for the sum, say... a double called 'sum' set to zero, and an integer 'count', also set to zero.
Then we write our loops. The outer loop handles the rows. It will run from the first row to the last row. Inside that, a second, nested loop will handle the columns. For each row, this inner loop will walk across all the columns.
Inside this inner loop is where the magic happens. We access the element at the current row and column... and add its value to our 'sum' variable. We also increment our 'count' variable.
After both loops have finished, we've visited every single grade! The final step is just to divide the total sum by the count to get our average.
Now, a really common mistake I see students make is mixing up the row and column counts in the loop headers. They'll use the number of columns for the row loop, and the number of rows for the column loop. This can throw an error if your grid isn't a perfect square. So always remember: the outer loop for rows goes up to the array's length, and the inner loop for columns goes up to the length of the current row.
Traversing 2D arrays might seem tricky at first, but it's just a pattern. Once you see it, you'll use it everywhere. You've got this.
This will likely cause an `ArrayIndexOutOfBoundsException` if the array isn't a perfect square. `grid.length` is the number of rows; `grid[0].length` is the number of columns.
The outer loop for rows should go up to `grid.length`. The inner loop for columns should go up to `grid[i].length`.
// Incorrect
for (int i = 0; i < grid[0].length; i++) { // Used column count for rows
for (int j = 0; j < grid.length; j++) { // Used row count for columns
...
}
}
The array is still stored as `[row][column]`. The `i` variable is tracking the row index, and `j` is tracking the column index. Swapping them in the accessor (`grid[j][i]`) scrambles the data and will likely cause an exception.
Always access elements as `grid[i][j]`, where `i` is your row loop variable and `j` is your column loop variable, regardless of which loop is on the outside.
// Incorrect
for (int j = 0; j < numCols; j++) {
for (int i = 0; i < numRows; i++) {
System.out.print(grid[j][i]); // Wrong access order!
}
}
A 2D array like `int[][] grid` is an "array of int arrays". The elements of `grid` are not `int`s; they are `int[]`s. This will cause a compiler error.
The outer loop variable must be an array type that matches the inner arrays.
for (int[] row : grid) { // Correct
for (int val : row) { ... }
}
The loop variable `val` is a *copy* of the element's value. Assigning a new value to the copy does not affect the original array.
To modify an array, you must use a standard indexed `for` loop so you can access the element by its position: `grid[i][j] = 100;`.
// Incorrect
for (int[] row : grid) {
for (int val : row) {
val = 100; // Does not change the grid
}
}
If an array has `N` elements, its indices are `0` to `N-1`. Using `<=` will cause the loop to try to access index `N` on its last iteration, resulting in an `ArrayIndexOutOfBoundsException`.
Always use `<` in standard `for` loop conditions when iterating from 0: `for (int i = 0; i < grid.length; i++)`.
// Incorrect
for (int i = 0; i <= grid.length; i++) { ... }