The whole class's marksheet
Riya's report-card program works for one student. Now the class teacher wants it for everyone: 3 students, 4 subjects each. Riya starts writing marksRiya, marksKabir, marksMeera… and stops. There are 60 students in the real class.
Sir, it's a table: students down, subjects across. Can one array hold a whole table?
Yes, with two indexes: marks[student][subject]. Kabir's English is marks[1][2]: row first, then column.
So Java stores a grid of boxes?
Here's the secret: Java has no real grid. A 2D array is an array whose elements are themselves arrays, one per row. That's why marks[1] on its own is a whole row, and why rows can even have different lengths.
What is a two-dimensional array?
A two-dimensional array in Java is an array of arrays: a one-dimensional array whose elements are references to other one-dimensional arrays (the rows). An element is accessed with two indexes, a[row][col]. Because each row is a separate array object, rows may have different lengths; such an array is called a jagged (or ragged) array.
A list of rows, where each row is a normal array. First pick the row, then the box inside that row.
Words you'll meet in this lesson
a[r].a[0][c], a[1][c] …2D array facts at a glance
| Create rectangular | int[][] a = new int[3][4]; → 3 rows × 4 columns, all 0 |
|---|---|
| Create with values | int[][] a = {{1, 2, 3}, {4, 5, 6}}; |
| Number of rows | a.length |
| Length of row r | a[r].length (use this, not a fixed number) |
| Jagged | int[][] a = new int[3][]; then a[0] = new int[2]; … (rows start as null) |
Arrays.deepToString(a) → [[1, 2, 3], [4, 5, 6]] |
Exam tip: the first size is required, the second is optional: new int[3][] compiles, new int[][3] does not. Also, new int[3][4] creates 4 objects: one outer array and three rows.
Rectangular and jagged arrays
Rectangular (matrix)
Every row has the same number of columns. Marksheets, seat maps, game boards, images.
new int[rows][cols]Jagged
Rows of different lengths: days in each month, orders per customer, seats per coach.
new int[rows][]Rows are arrays
a[1] is a full int[]. You can pass it to a method, loop over it with for-each, or swap whole rows.
A jagged array in action
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[][] seats = new int[3][]; // 3 rows, not created yet
System.out.println(Arrays.deepToString(seats));
seats[0] = new int[1];
seats[1] = new int[3];
seats[2] = new int[2];
System.out.println(Arrays.deepToString(seats));
System.out.println("Row 1 has " + seats[1].length + " seats");
}
}Which loop shape do I need?
| Task | Outer loop | Inner loop |
|---|---|---|
| Print, row totals, search | rows: r < a.length | columns: c < a[r].length |
| Column totals | columns: c < a[0].length | rows: r < a.length |
| Diagonal of a square matrix | one loop: a[i][i] and a[i][n - 1 - i] | |
| Just read every value | for (int[] row : a) | for (int x : row) |
Copy trap: a.clone() and Arrays.copyOf(a, a.length) copy only the outer array. The new array points to the same rows, so changing copy[0][0] also changes a[0][0]. Copy each row for a real (deep) copy.
Building and walking a 2D array
Create the table
The JVM creates an outer array of 3 references, then threeint[][] grid = new int[3][4];int[4]row arrays filled with 0, and stores each row's address in the outer array.Read or write one cell
Two steps:grid[1][2] = 7;grid[1]follows the outer array to row 1;[2]then picks index 2 in that row. Both indexes are bounds-checked.Loop over rows, then over each row
for (int r = 0; r < grid.length; r++) { for (int c = 0; c < grid[r].length; c++) { System.out.print(grid[r][c] + " "); } System.out.println(); }grid.lengthis the number of rows;grid[r].lengthis that row's length. This form works for rectangular and jagged arrays alike.Or use for-each when you only read
The outer for-each hands you each row as anfor (int[] row : grid) { for (int value : row) total += value; }int[]. That's the array-of-arrays idea made visible.Print it for debugging
System.out.println(Arrays.deepToString(grid));Arrays.toString(grid)would print three row references like[I@1b6d3586;deepToStringgoes inside each row.
The array-of-arrays secret: both views
Left: the code and what it prints. Right: the outer array of references and the row arrays it points to.
int[][] 6 steps- Create:
new int[3][4]gives 3 rows of 4 zeros. - Write:
grid[1][2] = 7sets row 1, column 2. - Sizes:
grid.lengthcounts rows;grid[1].lengthcounts the columns in row 1. - Traverse: nested loops print the table row by row.
- Jagged:
new int[3][]creates only the list of rows; each isnullfor now. - Rows of any length: each row is created separately, so their sizes can differ.
- There's no single grid in memory: an outer array holds three references, and each points to its own
int[4]row. - The JVM first reads
grid[1](a reference), follows it to row 1, then reads index 2 there. grid.lengthis the outer array's length.grid[1].lengthbelongs to the row object.- Visiting rows in order keeps the JVM reading one row's memory at a time, which is the fast direction.
new int[3][]creates the outer array only. Usingseats[0][0]now would throwNullPointerException.- Rows created later with
new int[2],new int[4],new int[3]: a jagged array.
2D arrays in real systems
Whenever data has rows and columns, a 2D array (or something built like one) is behind it.
Watch it with an example: a class marks sheet
3 students × 3 subjects in one int[][]. Watch the nested loops add each row, and see how Java really stores it: an array of row arrays.
- The table:
marks[r][c]is student r's mark in subject c.marks[1][2]is Kabir's Chem mark, 72. - Outer loop r = 0: the inner loop walks across Riya's row: 78 + 85 + 90 = 253.
- r = 1: Kabir's row: 65 + 70 + 72 = 207. The inner loop always restarts at column 0.
- r = 2: Asha's row: 88 + 92 + 95 = 275.
- Result: the highest total is Asha's 275. School ERPs and report cards work on exactly this shape of data.
- Behind the screen: Java has no single grid.
marksrefers to an array of 3 references, and each points to its own row array. That's why rows can even have different lengths (jagged arrays).
1. Seat maps
Cinema halls, buses and flights keep seat status in a grid: seat[row][col]. Different coach types, with different seats per row, are a natural jagged array.
2. Reports and pivot tables
Sales by store and week, attendance by student and day, expenses by month and category: rows, columns, and totals along each.
3. Images
A grayscale image is a 2D array of brightness values, pixel[y][x]. Filters like blur and sharpen loop over every row and column.
4. Game boards and maps
Tic-tac-toe, chess, Sudoku, and the tile maps of 2D games are grids. Checking a row, column or diagonal for a win is a 2D-array loop.
Examples are simplified. Large tables usually live in a database or spreadsheet, and are loaded into arrays or lists for processing.
The class marksheet
public class Main {
public static void main(String[] args) {
String[] students = {"Riya", "Kabir", "Meera"};
int[][] marks = {
{78, 92, 65, 88},
{81, 67, 90, 74},
{95, 88, 79, 91}
};
for (int s = 0; s < marks.length; s++) {
int total = 0;
for (int sub = 0; sub < marks[s].length; sub++) {
total += marks[s][sub];
}
System.out.println(students[s] + " total: " + total);
}
System.out.println("Kabir's English: " + marks[1][2]);
}
}int[][] marks = { {…}, {…}, {…} };Each inner pair of braces is one row (one student).String[] studentsA parallel array:students[s]names rows.int total = 0;inside the outer loopReset for every student, otherwise totals pile up.marks[s].lengthThe number of subjects in this student's row.
Summing a 2 × 2 matrix
Watch r stay put while c runs across each row, and the three objects on the heap.
Worked examples: real problems, solved and traced in memory
Three real problems that need rows and columns. Run each solution, then step through it to watch the outer array, the row arrays and every method call in memory.
Total sales per store
Problem statement
A chain has 2 stores. Their sales (in ₹ thousands) for 3 weeks are kept in a 2D array, one row per store. Print each store's total using a helper method that adds up one row.
sales = {{120, 135, 110}, {90, 95, 100}}Store 1: 365 Store 2: 285
Approach
- Each row of a 2D array is itself an
int[], so a method that sums anint[]can sum a row. - Loop over the rows with
s; passsales[s](one row) torowTotal. - Print the returned total for each store.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Book a seat, refuse if taken
Problem statement
A small screen has 2 rows of 3 seats. A method book(seats, row, col) must mark a free seat as booked and return true, or return false if someone already has it. Try booking row 1 seat 2, the same seat again, then row 0 seat 0.
seats = new boolean[2][3] (all free) requests: (1, 2), (1, 2), (0, 0)
R1 S2: true R1 S2 again: false R0 S0: true
Approach
- Use
boolean[][]:false= free (the default),true= booked. - In
book, checkseats[row][col]; if already true, return false. - Otherwise set it to true and return true. The change is made in the shared array, so main sees it.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Average marks in classes of different sizes
Problem statement
Class A has 2 students and class B has 3. Their marks are stored in a jagged array (rows of different lengths). Print each class's average mark.
marks = {{70, 80}, {90, 60, 75}}Class A: 75.0 Class B: 75.0
Approach
- A jagged array lets each row (class) have its own length.
- For row
c, loop up tomarks[c].length, not a fixed number. - Average = row sum / row length, as a double.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Check yourself
Quick revision
Lesson 4.2 in one screen
- A 2D array is an array of arrays: an outer array of row references.
a[row][col]: row first.a.length= rows,a[r].length= that row's columns.new int[r][c]→ r + 1 objects, all zeros.new int[r][]→ rows are null (jagged).- Traverse: outer over rows, inner up to
a[r].length. Columns first for column totals. - Diagonals need one loop:
a[i][i],a[i][n-1-i]. - Print with
Arrays.deepToString. clone()/copyOfare shallow; copy every row for a deep copy.
Interview and viva questions
Are Java 2D arrays stored as one block of memory?
No. Unlike C, Java stores a 2D array as an outer array of references, each pointing to a separate row array. Rows may be anywhere on the heap and may have different lengths.
What is a jagged array and when is it useful?
A 2D array whose rows have different lengths, created as new int[n][] with each row created separately. Useful for naturally uneven data: days per month, items per order, seats per coach. It saves memory compared with padding every row to the longest length.
How do you deep-copy a 2D array?
Create a new outer array, then copy each row: for (int r = 0; r < a.length; r++) copy[r] = a[r].clone();. For arrays of objects, you'd also need to copy the objects themselves.
Why is row-by-row traversal usually faster than column-by-column?
Each row is a contiguous array, so reading along a row uses memory that is already in the CPU cache. Going down a column jumps between different row objects every step. For big matrices the difference can be noticeable.
How do you pass a 2D array to a method and what can the method change?
Declare the parameter as int[][] a. The method receives a copy of the reference, so it can change any element (and even replace rows) of the caller's array, but reassigning a itself inside the method doesn't affect the caller.
Practice problems
Matrix problems appear in almost every campus test. For each one, first decide: do I go row by row, column by column, or along a diagonal? The last three are real business tasks.
Print a matrix as a grid
Print a 2D array as rows and columns, with values separated by tabs.
m = {{1, 2, 3}, {4, 5, 6}}1 2 3 4 5 6
Show solution and explanation
public class Main {
public static void main(String[] args) {
int[][] m = {{1, 2, 3}, {4, 5, 6}};
for (int r = 0; r < m.length; r++) {
for (int c = 0; c < m[r].length; c++) {
System.out.print(m[r][c] + "\t");
}
System.out.println();
}
}
}How it works: the outer loop picks a row, the inner loop walks across it. Using m[r].length (not a fixed number) keeps it correct for jagged arrays too. println() after the inner loop ends each row.
Row sums
Print the total of each row.
m = {{3, 5, 2}, {7, 1, 4}, {6, 6, 6}}Row 0 sum = 10 Row 1 sum = 12 Row 2 sum = 18
Show solution and explanation
public class Main {
public static void main(String[] args) {
int[][] m = {{3, 5, 2}, {7, 1, 4}, {6, 6, 6}};
for (int r = 0; r < m.length; r++) {
int sum = 0;
for (int c = 0; c < m[r].length; c++) {
sum += m[r][c];
}
System.out.println("Row " + r + " sum = " + sum);
}
}
}How it works: reset sum to 0 at the start of every row, inside the outer loop. Declaring it outside both loops is the most common bug: the totals would keep piling up.
Column sums
Print the total of each column.
m = {{3, 5, 2}, {7, 1, 4}, {6, 6, 6}}Column 0 sum = 16 Column 1 sum = 12 Column 2 sum = 12
Show solution and explanation
public class Main {
public static void main(String[] args) {
int[][] m = {{3, 5, 2}, {7, 1, 4}, {6, 6, 6}};
for (int c = 0; c < m[0].length; c++) {
int sum = 0;
for (int r = 0; r < m.length; r++) {
sum += m[r][c];
}
System.out.println("Column " + c + " sum = " + sum);
}
}
}How it works: swap the loops: the outer loop goes over columns, the inner over rows. The index order m[r][c] stays the same; only the loop order changes. This assumes a rectangular matrix.
Diagonal sums
For a square matrix, find the sum of the main diagonal (top-left to bottom-right) and the other diagonal.
m = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}Main diagonal: 15 Other diagonal: 15
Show solution and explanation
public class Main {
public static void main(String[] args) {
int[][] m = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int n = m.length, main = 0, other = 0;
for (int i = 0; i < n; i++) {
main += m[i][i];
other += m[i][n - 1 - i];
}
System.out.println("Main diagonal: " + main);
System.out.println("Other diagonal: " + other);
}
}How it works: on the main diagonal row and column are equal, [i][i]. On the other diagonal they add up to n − 1, so the column is n - 1 - i. One loop is enough; no nested loop needed.
Add two matrices
Add two matrices of the same size element by element into a new matrix.
a = {{1, 2}, {3, 4}}, b = {{10, 20}, {30, 40}}[[11, 22], [33, 44]]
Show solution and explanation
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[][] a = {{1, 2}, {3, 4}};
int[][] b = {{10, 20}, {30, 40}};
int[][] sum = new int[a.length][a[0].length];
for (int r = 0; r < a.length; r++) {
for (int c = 0; c < a[0].length; c++) {
sum[r][c] = a[r][c] + b[r][c];
}
}
System.out.println(Arrays.deepToString(sum));
}
}How it works: create the result with the same dimensions, then fill each cell from the matching cells of a and b. Arrays.deepToString prints nested arrays; Arrays.toString would print only the row references.
Transpose a matrix
Turn rows into columns: a 2 × 3 matrix becomes 3 × 2.
m = {{1, 2, 3}, {4, 5, 6}}[[1, 4], [2, 5], [3, 6]]
Show solution and explanation
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[][] m = {{1, 2, 3}, {4, 5, 6}};
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];
}
}
System.out.println(Arrays.deepToString(t));
}
}How it works: the result has the dimensions swapped: new int[cols][rows]. Each value moves from [r][c] to [c][r].
Largest value in each row
Print the highest value in each row of a jagged array (rows of different lengths).
scores = {{45, 80}, {72, 91, 60}, {55}}Row 0: 80 Row 1: 91 Row 2: 55
Show solution and explanation
public class Main {
public static void main(String[] args) {
int[][] scores = {{45, 80}, {72, 91, 60}, {55}};
for (int r = 0; r < scores.length; r++) {
int max = scores[r][0];
for (int c = 1; c < scores[r].length; c++) {
if (scores[r][c] > max) max = scores[r][c];
}
System.out.println("Row " + r + ": " + max);
}
}
}How it works: each row is its own array with its own length, so the inner loop must use scores[r].length. Using scores[0].length would skip values in row 1 and crash on row 2.
Search in a matrix
Find the row and column of a value, or report that it isn't there. Stop as soon as it's found.
m = {{4, 9, 2}, {7, 5, 8}}, target = 5Found at row 1, column 1
Show solution and explanation
public class Main {
public static void main(String[] args) {
int[][] m = {{4, 9, 2}, {7, 5, 8}};
int target = 5;
boolean found = false;
search:
for (int r = 0; r < m.length; r++) {
for (int c = 0; c < m[r].length; c++) {
if (m[r][c] == target) {
System.out.println("Found at row " + r + ", column " + c);
found = true;
break search;
}
}
}
if (!found) System.out.println("Not found");
}
}How it works: a labelled break (Lesson 3.5) leaves both loops at once. The found flag tells us afterwards whether to print “Not found”.
Store × week sales report
A chain has 3 stores. Sales (in ₹ thousands) for 4 weeks are in a 2D array, one row per store. Print each store's total, each week's total and the best store.
sales = {{120, 135, 110, 150}, {90, 95, 100, 98}, {160, 140, 155, 170}}Andheri: 515 Pune: 383 Indiranagar: 625 Week totals: 370 370 365 418 Best store: Indiranagar
Show solution and explanation
public class Main {
public static void main(String[] args) {
String[] stores = {"Andheri", "Pune", "Indiranagar"};
int[][] sales = {{120, 135, 110, 150}, {90, 95, 100, 98}, {160, 140, 155, 170}};
int best = 0, bestTotal = -1;
for (int s = 0; s < sales.length; s++) {
int total = 0;
for (int w = 0; w < sales[s].length; w++) total += sales[s][w];
System.out.println(stores[s] + ": " + total);
if (total > bestTotal) { bestTotal = total; best = s; }
}
System.out.print("Week totals:");
for (int w = 0; w < sales[0].length; w++) {
int total = 0;
for (int s = 0; s < sales.length; s++) total += sales[s][w];
System.out.print(" " + total);
}
System.out.println();
System.out.println("Best store: " + stores[best]);
}
}How it works: a parallel 1D array stores holds the names, so stores[s] labels row s. Row totals walk across; week totals walk down. This is exactly what a spreadsheet pivot table does.
Bus seat booking map
A bus has 5 rows of 4 seats. true means booked. Print the seat map (X = booked, _ = free), count free seats, and book the first free window seat (columns 0 and 3).
5 × 4 boolean array with some seats booked
X X _ X X _ _ X _ _ X _ _ _ _ _ _ _ _ _ Free seats: 14 Booked window seat: row 3, seat 1
Show solution and explanation
public class Main {
public static void main(String[] args) {
boolean[][] booked = new boolean[5][4];
booked[0][0] = booked[0][1] = booked[0][3] = true;
booked[1][0] = booked[1][3] = true;
booked[2][2] = true;
int free = 0;
for (int r = 0; r < booked.length; r++) {
for (int c = 0; c < booked[r].length; c++) {
System.out.print(booked[r][c] ? "X " : "_ ");
if (!booked[r][c]) free++;
}
System.out.println();
}
System.out.println("Free seats: " + free);
windowSearch:
for (int r = 0; r < booked.length; r++) {
for (int c = 0; c < 4; c += 3) {
if (!booked[r][c]) {
booked[r][c] = true;
System.out.println("Booked window seat: row " + (r + 1) + ", seat " + (c + 1));
break windowSearch;
}
}
}
}
}How it works: a new boolean[][] starts all false (free). c += 3 visits only columns 0 and 3, the windows. Seat numbers shown to passengers are index + 1. Real booking apps use the same grid idea, with the database deciding who gets a seat when two people click together.
Attendance shortage list
Rows are students, columns are 6 class days (1 = present, 0 = absent). Print each student's attendance percentage and flag anyone below 75%.
att = {{1,1,1,1,0,1}, {1,0,0,1,0,1}, {1,1,1,1,1,1}}Riya 83.3% Kabir 50.0% << shortage Meera 100.0%
Show solution and explanation
public class Main {
public static void main(String[] args) {
String[] names = {"Riya", "Kabir", "Meera"};
int[][] att = {{1, 1, 1, 1, 0, 1}, {1, 0, 0, 1, 0, 1}, {1, 1, 1, 1, 1, 1}};
for (int s = 0; s < att.length; s++) {
int present = 0;
for (int d = 0; d < att[s].length; d++) present += att[s][d];
double pct = present * 100.0 / att[s].length;
String flag = pct < 75 ? " << shortage" : "";
System.out.printf("%-6s %5.1f%%%s%n", names[s], pct, flag);
}
}
}How it works: with 1/0 values, the row sum is the number of days present. Multiplying by 100.0 forces decimal division. printf lines the names and percentages up in columns (%% prints a percent sign).
Real-world scenario questions (asked in company interviews)
Grid bugs are usually row/column mix-ups, uneven rows or shared rows. Decide your answer first, then open the model answer.
Works on square photos, crashes on landscape ones
An image filter stores pixels as int[height][width]. It works on 500 × 500 test images, but crashes with ArrayIndexOutOfBoundsException on a 800 × 600 landscape photo. The loop is for (x < width) for (y < height) pixel[x][y]. What's wrong?
The interviewer is checking: do you keep row/column (y/x) order straight, and test with non-square data?
Show a strong answer
- The array is
[height][width], so it must be indexedpixel[y][x]. The code swaps them. - With square images, both dimensions are equal, so the bug is invisible. With 800 wide × 600 high,
xreaches 600+ as a row index, and there are only 600 rows. - Fix the index order, loop rows (y) on the outside, and add a non-square test image to the test suite.
Avoid: testing only with square or symmetric data. It hides row/column mix-ups.
New stores have fewer weeks of data
A report keeps weekly sales in sales[store][week]. Two stores opened recently, so their rows are shorter. The report now either crashes or misses weeks for older stores. The inner loop uses sales[0].length. Explain and fix.
The interviewer is checking: do you understand jagged arrays and per-row lengths?
Show a strong answer
- Rows have different lengths now. Using row 0's length for every row overruns shorter rows (crash) or stops early on longer ones (missing data).
- Use
sales[s].lengthin the inner loop so each store uses its own number of weeks. - For week-wise totals across stores, check
w < sales[s].lengthbefore reading, and decide with the business how to treat weeks before a store opened.
Avoid: padding rows with zeros without asking. Zero sales and “not open yet” mean different things in a report.
Undo doesn't undo
A board game saves the board before every move with history.push(board.clone()), where board is a char[8][8]. When the player presses Undo, the restored board still shows the latest move. Why?
The interviewer is checking: do you know shallow vs deep copies of nested arrays?
Show a strong answer
clone() copied only the outer array of 8 row references. Every saved snapshot shares the same 8 row arrays as the live board, so each move changes all the “saved” boards too.
- Deep-copy the rows: create a new
char[8][]and setsnapshot[r] = board[r].clone()for each row. - Alternatively, store the moves (from, to, captured piece) and reverse them, which uses far less memory.
- Add a test: make a move, undo, and compare with the original board.
Avoid: assuming clone() copies everything. For nested arrays and objects it's shallow.
Same work, five times slower
Two versions of a job sum a 10,000 × 10,000 double[][]. Version A loops rows outside, columns inside. Version B loops columns outside, rows inside. Both are correct, but B is several times slower. Explain.
The interviewer is checking: do you understand memory layout and CPU caches?
Show a strong answer
- Each row is a separate contiguous array. Version A reads each row from start to end, so the CPU fetches memory in cache lines and uses all of it.
- Version B jumps to a different row object on every step, touching one value per cache line and constantly fetching new memory.
- Prefer row-major traversal. If you truly need column-wise access often, store the data transposed, or as a single 1D array indexed
r * cols + c. - Always measure with a proper benchmark before and after.
Avoid: saying “both are O(n²), so they're the same speed.” Big-O ignores memory effects.