المصفوفات ثنائية الأبعاد
في هذا الدرس 11 موضوعات
هذا الدرس متاح حاليًا باللغة الإنجليزية فقط، ولم تُنشر نسخته العربية بعد.
Lesson Overview
Some data is naturally a table: a timetable with days and hours, a spreadsheet of sales by region and month, a seating plan with rows and seats. One row of lockers is not enough; you need a wall of lockers with rows and columns, where you name a locker with two numbers: "row 2, column 3".
Java has no special table type. A two-dimensional array is simply an array whose elements are themselves arrays: an outer row of lockers, each locker holding a whole row of lockers. This tiny idea explains every rule of this lesson, including the odd ones.
Why this matters: backend programs process tabular data all the time, such as query results, CSV files, and reports. The array-of-arrays shape, and the nested loops that walk it, are the base for all of it.
This lesson builds on arrays and loops (Lessons 1-2) and nested loops (BEG-013-L05).
Learning Objectives
By the end of this lesson you will be able to:
- Create a two-dimensional array with
newand with nested initializer lists. - Read and write a cell with
grid[row][col]. - Explain what
grid.lengthandgrid[r].lengthmean. - Visit every cell with nested loops and compute row, column, and overall totals.
- Describe a two-dimensional array as an array of arrays, and explain what a row that is
nullmeans. - Avoid mixing up rows and columns when choosing loop bounds.
Written Explanation
An array of arrays
int[][] grid = new int[3][4];Read int[][] as "array of arrays of int". This creates 3 rows, each with 4 columns: 12 cells, all starting at 0. The Java Language Specification describes multi-dimensional arrays in just this way: the elements of the outer array are references to sub-arrays.
grid ---> [ row0 ] ---> [ 0 | 0 | 0 | 0 ]
[ row1 ] ---> [ 0 | 0 | 0 | 0 ]
[ row2 ] ---> [ 0 | 0 | 0 | 0 ]You can also write the values out. Each inner pair of braces is one row:
int[][] sales = {
{120, 80, 95},
{60, 110, 70}
}; // 2 rows, 3 columnsIndexing: row first, then column
grid[r][c] means "go to row r, then to column c in that row". The row comes first. Both indices start at 0, and each follows the rule of Lesson 1: valid rows are 0 to grid.length - 1, valid columns are 0 to grid[r].length - 1.
Two sizes to remember:
grid.lengthis the number of rows (the length of the outer array).grid[r].lengthis the number of columns in rowr(the length of one inner array).
For new int[3][4], grid.length is 3 and grid[0].length is 4.
Visiting every cell: nested loops
Use one loop for the rows and one inside it for the columns:
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[r].length; c++) {
// grid[r][c] is the current cell
}
}Using grid[r].length (not a fixed number) for the inner bound keeps the code correct when the table size changes. When you only read the values, the enhanced for works twice, once per level:
for (int[] row : grid) {
for (int v : row) {
// v is one value
}
}The outer loop's variable has type int[], one whole row, which is exactly what the outer array contains.
Row totals, column totals, diagonals
- A row total adds
grid[r][c]for allcwithrfixed. The sum variable is reset at the start of each row, or each total goes into its own array element. - A column total fixes
cand loops overr:grid[r][c]for allr. - For a square grid, the main diagonal is
grid[i][i]: a single loop is enough.
Rows can be created separately
Because rows are independent arrays, new int[3][] creates only the outer array with three empty row slots, each null for now. You then create each row yourself, and the rows can even have different lengths (a ragged array):
int[][] tri = new int[4][];
tri[2] = new int[3]; // row 2 now exists with three columnsUsing a row that has not been created is a NullPointerException, not an index error, because the row slot holds null. Most of the time you will use the new int[r][c] form, where all rows are created for you.
Examples
Example 1: build and print a table (simple)
public class Ex1 {
public static void main(String[] args) {
int[][] grid = new int[3][4];
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[r].length; c++) {
grid[r][c] = r * c;
}
}
System.out.println(grid.length);
System.out.println(grid[0].length);
System.out.println(grid[2][3]);
for (int[] row : grid) {
for (int v : row) {
System.out.print(v + " ");
}
System.out.println();
}
}
}Output (compiled and run with JDK 21):
3
4
6
0 0 0 0
0 1 2 3
0 2 4 6 grid[2][3] is row 2, column 3: 2 * 3 = 6. The inner loop prints one row; println() with no argument ends the line.
Example 2: monthly sales by shop (practical)
Rows are shops, columns are months.
public class Ex2 {
public static void main(String[] args) {
int[][] sales = {
{120, 80, 95},
{60, 110, 70}
};
int total = 0;
int[] rowTotals = new int[sales.length];
for (int r = 0; r < sales.length; r++) {
for (int c = 0; c < sales[r].length; c++) {
rowTotals[r] += sales[r][c];
total += sales[r][c];
}
}
System.out.println("Row 0: " + rowTotals[0] + ", row 1: " + rowTotals[1]);
System.out.println("All: " + total);
int col1 = 0;
for (int r = 0; r < sales.length; r++) {
col1 += sales[r][1];
}
System.out.println("Column 1: " + col1);
}
}Output:
Row 0: 295, row 1: 240
All: 535
Column 1: 190One nested loop produced both the row totals (stored in a one-dimensional array with one slot per row) and the grand total. The column total needs only one loop: the column is fixed at 1 and the row varies.
Example 3: a ragged table and a missing row (realistic)
public class Ex3 {
public static void main(String[] args) {
int[][] tri = new int[4][];
for (int r = 0; r < tri.length; r++) {
tri[r] = new int[r + 1];
for (int c = 0; c < tri[r].length; c++) {
tri[r][c] = c + 1;
}
}
for (int[] row : tri) {
for (int v : row) {
System.out.print(v + " ");
}
System.out.println();
}
int[][] pending = new int[2][];
System.out.println(pending[0]);
System.out.println(pending[0].length);
}
}Output (compiled with javac -g so the message shows variable names):
1
1 2
1 2 3
1 2 3 4
null
Exception in thread "main" java.lang.NullPointerException: Cannot read the array length because "pending[0]" is null
at Ex3.main(Ex3.java:18)Each row was created with its own length, so row r has r + 1 columns and the inner bound tri[r].length handles it automatically. In pending, the rows were never created, so pending[0] is null, and asking for its length fails with a NullPointerException rather than an index error.
Backend Insight
A database result, a CSV file, and a spreadsheet are all rows and columns, and a two-dimensional array is the most literal way to hold one in memory. In practice, backend code usually maps each row to an object with named fields rather than a bare [row][col] number, because data[3][2] says nothing about what column 2 means. Keep that in mind: tables of numbers fit grids well (scores, image pixels, game boards, matrices), while mixed data such as a name, an age, and an email belongs in objects, which you will build in the object-oriented modules.
One more connection: because a 2D array is an array of arrays, copying it with a simple copy duplicates only the outer row of pointers, so two copies would still share the rows. The next lessons and the exercises return to this.
Common Mistakes
Mistake 1: Swapping row and column
grid[3][2] on a 3-row, 4-column grid asks for row 3, which does not exist. Row first, column second, and the row bound is grid.length.
Mistake 2: Using the wrong length for a bound
- Incorrect: inner loop
c < grid.length(that is the row count) - Correct: inner loop
c < grid[r].length
On a non-square grid the first version either skips cells or crashes.
Mistake 3: Not resetting the row total
If the row-total variable is declared outside the outer loop, it keeps adding across rows. Declare it inside the outer loop (or use one array slot per row).
Mistake 4: Using a row that was never created
After new int[2][], grid[0][0] throws a NullPointerException. Create the row first with grid[0] = new int[3];.
Mistake 5: Assuming grid.length is the total number of cells
It is only the number of rows. The total is rows times columns for a regular grid, or the sum of the row lengths for a ragged one.
Exercises
Try each exercise yourself before opening the solutions.
Level A: Foundation
A1. Write one statement that creates a grid of 2 rows and 3 columns of int, and one that creates the identity grid with rows {1, 0} and {0, 1}.
أظهر الحل A1
A1. int[][] g = new int[2][3]; and int[][] id = {{1, 0}, {0, 1}};.
A2. For int[][] g = new int[5][2];, what are g.length, g[0].length, and the number of cells? What is the largest valid g[r][c]?
أظهر الحل A2
A2. g.length is 5, g[0].length is 2, there are 10 cells, and the largest valid cell is g[4][1].
A3. Which cell does m[1][2] name if m is a table of seats where rows are rows of the cinema and columns are seats?
أظهر الحل A3
A3. Row 1, column 2: the second row of the cinema (counting from 0) and the third seat in it.
Level B: Application
B1. For a square int[][] m write a loop that sums the main diagonal.
أظهر الحل B1
B1.
int sum = 0;
for (int i = 0; i < m.length; i++) {
sum += m[i][i];
}B2. Write a method static int columnSum(int[][] m, int c) that returns the sum of column c.
أظهر الحل B2
B2.
static int columnSum(int[][] m, int c) {
int sum = 0;
for (int r = 0; r < m.length; r++) {
sum += m[r][c];
}
return sum;
}It assumes every row has a column c; for a ragged table, add a check on m[r].length.
B3. Print m as text, one row per line, with the values of a row separated by a space and no trailing space.
أظهر الحل B3
B3.
for (int r = 0; r < m.length; r++) {
for (int c = 0; c < m[r].length; c++) {
if (c > 0) {
System.out.print(" ");
}
System.out.print(m[r][c]);
}
System.out.println();
}Printing the separator before every value except the first avoids a trailing space.
Level C: Challenge
C1. Write a method that returns the transpose of a 2-row, 3-column int[][] (the result has 3 rows and 2 columns, and result[c][r] = m[r][c]).
أظهر الحل C1
C1.
static int[][] transpose(int[][] m) {
int[][] t = new int[3][2];
for (int r = 0; r < 2; r++) {
for (int c = 0; c < 3; c++) {
t[c][r] = m[r][c];
}
}
return t;
}Note the new array has the dimensions swapped. For other sizes, use m.length and m[0].length instead of 2 and 3.
C2. A cinema is a boolean[][] taken (true means the seat is taken). Write static int freeSeats(boolean[][] taken) that counts the free seats even if rows have different lengths, and explain why taken.length * taken[0].length is not safe.
أظهر الحل C2
C2.
static int freeSeats(boolean[][] taken) {
int free = 0;
for (boolean[] row : taken) {
for (boolean seat : row) {
if (!seat) {
free++;
}
}
}
return free;
}taken.length * taken[0].length assumes every row is as long as row 0 and that row 0 exists. With ragged rows it gives a wrong count, and with an empty taken it fails on taken[0].
Mini Challenge
Make a weekly timetable as String[][] with 5 rows (days) and 4 columns (periods). Fill in a few subjects and leave the other cells as null. Write a program that prints each day on one line, writes free for every empty cell, and finally prints how many free periods there are in total and the day with the most free periods.
Check your work: the outer bound is length, the inner bound is [r].length, and you tested for null before treating a cell as text.
Summary & Next Lesson
Key takeaways
- A two-dimensional array is an array of arrays:
new int[rows][cols]. grid[r][c]is row first, then column.grid.lengthcounts rows andgrid[r].lengthcounts the columns of rowr.- Nested loops visit every cell; use
grid[r].lengthas the inner bound. - Row totals fix the row, column totals fix the column, and a square grid's diagonal is
grid[i][i]. - Rows can be created separately or be different lengths; a row that was never created is
null.
Next lesson
The next lesson, "Working with Strings", turns from numbers in lockers to text: the String methods you use every day to measure, cut, search, and clean text.
Self-check
- In
grid[2][3], which number is the row? - What is
grid[0].lengthfornew int[3][4]? - Why use
grid[r].lengthfor the inner loop bound? - How do you sum column
cof a grid? - What goes wrong with
new int[2][]and thengrid[0][0] = 1;?
أظهر الإجابات
- The first one: 2 is the row.
- 4.
- It adapts to the actual row length, so the code keeps working when the table changes size or the rows differ in length.
- Loop
rover all rows and addgrid[r][c]. - Row 0 was never created, so it is
nulland the assignment throws aNullPointerException.
References
- Oracle, Java Language Specification SE 21, chapter 10: Arrays. It supports: an array's component type can itself be an array type, so multi-dimensional arrays are arrays whose elements refer to sub-arrays, and nested initializers create them.
- The outputs and the
NullPointerExceptionmessage were produced by compiling and running the code with JDK 21. The wall-of-lockers picture is this course's own teaching device.
التطبيق
طبّق هذا الدرس بمختبر الوحدة وتمارينها واختبارها.
تعليم الدرس كمكتمل اختياري ويمكنك التراجع عنه في أي وقت. يبقى تقدمك محفوظًا في هذا المتصفح.