CodeGym /Các khóa học /JAVA 25 SELF /Mảng răng cưa (Jagged Arrays)

Mảng răng cưa (Jagged Arrays)

JAVA 25 SELF
Mức độ , Bài học
Có sẵn

1. Mảng răng cưa khác mảng hai chiều

Chúng ta đã đến chủ đề mà nhiều người gọi là “mảng răng cưa” — tiếng Anh là jagged arrays. Khác với mảng hai chiều, mảng răng cưa cho phép lưu các cột có độ dài khác nhau. Tưởng tượng như một khu nhà, mỗi tòa có số căn hộ riêng — ở một tòa có 5 căn, tòa khác có 20 căn, còn tòa thứ ba chỉ có một căn.

Mảng răng cưa là mảng mà mỗi phần tử của nó là một mảng, và các mảng lồng nhau (còn gọi là “mảng con”) có thể có độ dài khác nhau.

Khác biệt chính:

  • Trong mảng hai chiều, mỗi “hàng” (và mỗi “cột”) có cùng số phần tử. Ví dụ: int[][] grid = new int[3][5]; — luôn có 3 hàng, mỗi hàng 5 phần tử.
  • Trong mảng răng cưa, mỗi hàng có thể có độ dài khác nhau! Ví dụ: int[][] jagged = new int[3][]; — và chỉ sau đó ta khởi tạo từng hàng (mảng con) theo cách riêng.

Minh họa trực quan:

Mảng hai chiều (3x3):
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
├───┼───┼───┤
│ 4 │ 5 │ 6 │
├───┼───┼───┤
│ 7 │ 8 │ 9 │
└───┴───┴───┘

Mảng răng cưa (độ dài khác nhau):
┌───┬───┐
│ 1 │ 2 │
├───┼───┼───┬───┐
│ 3 │ 4 │ 5 │ 6 │
├───┼───┴───┴───┘
│ 7 │
└───┘

2. Cú pháp khai báo và khởi tạo mảng răng cưa

Khai báo mảng răng cưa không hề khó hơn các kiểu trước! Đừng sợ dấu ngoặc vuông kép:


int[][] jaggedArray = new int[3][];
Khai báo mảng răng cưa: đã chỉ định kích thước bên ngoài, các mảng bên trong chưa được tạo

Điều đó có nghĩa là ta có một mảng gồm 3 phần tử, và mỗi phần tử cũng là một mảng int. Nhưng các mảng bên trong vẫn chưa được tạo! Để dễ hiểu hơn, hãy đi sâu hơn một chút.

Khởi tạo mảng răng cưa theo từng bước

Bước 1 — tạo mảng chính (bên ngoài):

int[][] jaggedArray = new int[3][];

Giờ ta có 3 “hàng”, nhưng tất cả hiện đều bằng null.

Bước 2 — tạo và cấp phát các mảng bên trong (mảng con):
Ví dụ, cho hàng thứ nhất dài 2, hàng thứ hai — 4, hàng thứ ba — 3:

jaggedArray[0] = new int[2]; // 2 phần tử ở hàng đầu tiên
jaggedArray[1] = new int[4]; // 4 phần tử ở hàng thứ hai
jaggedArray[2] = new int[3]; // 3 phần tử ở hàng thứ ba

Bước 3 — gán giá trị:
Các mảng bên trong chính là mảng bình thường! Ví dụ:

jaggedArray[0][0] = 1;
jaggedArray[0][1] = 2;

jaggedArray[1][0] = 3;
jaggedArray[1][1] = 4;
jaggedArray[1][2] = 5;
jaggedArray[1][3] = 6;

jaggedArray[2][0] = 7;
jaggedArray[2][1] = 8;
jaggedArray[2][2] = 9;

Khởi tạo ngắn gọn mảng răng cưa

Có thể tạo và điền giá trị ngay nếu bạn biết trước các giá trị:

int[][] jaggedArray = new int[][]
{
    new int[] { 1, 2 },
    new int[] { 3, 4, 5, 6 },
    new int[] { 7, 8, 9 }
};

Hoặc ngắn hơn một chút, bỏ qua kiểu của các mảng bên trong:

int[][] jaggedArray = 
{
    { 1, 2 },
    { 3, 4, 5, 6 },
    { 7, 8, 9 }
};

3. Duyệt và làm việc với mảng răng cưa

Duyệt mảng răng cưa không khó hơn mảng hai chiều, nhưng lúc này vòng ngoài đi qua các hàng, còn vòng trong đi qua các phần tử của hàng (có thể có độ dài khác nhau):

for (int i = 0; i < jaggedArray.length; i++) 
{
    System.out.println("Dòng " + i + ":");
    for (int j = 0; j < jaggedArray[i].length; j++) 
    {
        System.out.print(jaggedArray[i][j] + " ");
    }
    System.out.println();
}

Kết quả trên màn hình:

Dòng 0:
1 2 
Dòng 1:
3 4 5 6 
Dòng 2:
7 8 9 

Có thể dùng for-each để khỏi phải nghĩ về chỉ số:

for (int[] row : jaggedArray) 
{
    for (int value : row) 
    {
        System.out.print(value + " ");
    }
    System.out.println();
}

4. Tình huống sử dụng điển hình cho mảng răng cưa

Khi nào mảng răng cưa hữu ích hơn mảng hai chiều?

  • Khi bạn lưu cho mỗi người dùng số lượng dữ liệu khác nhau: điểm theo môn, mua sắm, bình luận, v.v.
  • Khi dữ liệu có cấu trúc tam giác hoặc bậc thang (ví dụ, để in hình kim tự tháp, tam giác Pascal, v.v.).
  • Khi muốn tiết kiệm bộ nhớ: trong mảng hai chiều, các hàng là cố định, còn với mảng răng cưa — chỉ cấp đúng số phần tử cần thiết.

Ví dụ thực tế: quản lý điểm của sinh viên

Giả sử có ba sinh viên, và đây là điểm của họ cho các bài tập khác nhau trong môn toán:

Sinh viên Điểm
0 5, 4
1 3, 4, 4
2 5

Khai báo mảng như sau:

int[][] studentMarks = new int[3][];
studentMarks[0] = new int[] { 5, 4 };         // Sinh viên thứ nhất — 2 điểm
studentMarks[1] = new int[] { 3, 4, 4 };      // Sinh viên thứ hai — 3 điểm
studentMarks[2] = new int[] { 5 };            // Sinh viên thứ ba — 1 điểm

In ra điểm của từng sinh viên:

for (int i = 0; i < studentMarks.length; i++) 
{
    System.out.print("Sinh viên " + i + ": ");
    for (int j = 0; j < studentMarks[i].length; j++) 
    {
        System.out.print(studentMarks[i][j] + " ");
    }
    System.out.println();
}

Sử dụng mảng răng cưa với các kiểu khác

Mảng răng cưa có thể là mảng của bất cứ thứ gì: chuỗi, mảng các mảng khác (sâu hơn!), thậm chí các đối tượng do bạn tự định nghĩa.

Ví dụ: mảng chuỗi

String[][] groups = {
    { "Ivan", "Pyotr" },
    { "Mariya", "Aleksey", "Sergey" },
    { "Vasilisa" }
};

5. Mảng ba chiều và mảng đa chiều

Thêm một điều thú vị về mảng mà có lẽ bạn đã đoán. Nếu đã có thể tạo mảng hai chiều, vậy có thể tạo mảng ba chiều không?

Có, bạn có thể tạo mảng với bất kỳ số chiều nào. Những mảng như vậy được gọi là mảng đa chiều.

Cách khai báo mảng đa chiều

Chỉ cần liệt kê số kích thước cần thiết trong các cặp ngoặc:

int[][][] cube = new int[2][3][4]; // 2 “lớp”, 3 hàng, 4 cột
cube[0][1][2] = 99;

Ở đây ta có mảng ba chiều:

  • 2 phần tử theo tọa độ thứ nhất,
  • 3 — theo tọa độ thứ hai,
  • 4 — theo tọa độ thứ ba.

Mảng như vậy là một “khối lập phương” dữ liệu lớn được sắp xếp liên tiếp.

Duyệt mảng ba chiều

Truy cập phần tử cần đủ mọi chỉ số ngay lập tức:

for (int i = 0; i < cube.length; i++) 
{
    for (int j = 0; j < cube[i].length; j++) 
    {
        for (int k = 0; k < cube[i][j].length; k++) 
        {
            System.out.print(cube[i][j][k] + " ");
        }
        System.out.println();
    }
    System.out.println("---");
}
  • Chỉ số bắt đầu từ 0, như thường lệ trong Java.
  • Tổng số phần tử trong mảng này là 2 × 3 × 4 = 24.

Ví dụ thực tế của mảng đa chiều

  • 2D — bảng, bàn cờ, hình ảnh.
  • 3D — “khối” trong đồ họa máy tính, dữ liệu cho tính toán khoa học (ví dụ, nhiệt độ ở các điểm khác nhau theo không gian và thời gian).
  • 4D trở lên — hiếm dùng, nhưng xuất hiện trong toán học nâng cao, mô phỏng, học máy, v.v.

6. Lỗi thường gặp khi làm việc với mảng đa chiều

Lỗi số 1: Vượt ra ngoài phạm vi mảng

Lỗi phổ biến nhất — cố truy cập vào phần tử không tồn tại, ví dụ:

int[][] arr = new int[2][3];
arr[2][0] = 5; // Lỗi! Không có hàng có chỉ số 2 (chỉ có 0 và 1)
arr[0][3] = 7; // Lỗi! Không có cột có chỉ số 3 (chỉ có 0, 1, 2)

Khi truy cập như vậy, chương trình sẽ ném ArrayIndexOutOfBoundsException. Luôn kiểm tra rằng chỉ số trong phạm vi hợp lệ: từ 0 đến length - 1.

Lỗi số 2: Hàng chưa được khởi tạo trong mảng răng cưa

Nếu tạo mảng răng cưa mà quên khởi tạo các mảng bên trong, khi truy cập sẽ gặp NullPointerException:

int[][] jagged = new int[3][];
jagged[0][0] = 5; // Lỗi! jagged[0] == null

Trước hết cần tạo mảng bên trong: jagged[0] = new int[2];

Lỗi số 3: Sử dụng sai độ dài của mảng

Dễ nhầm giữa matrix.length (số hàng) và matrix[0].length (số cột). Đặc biệt thường gặp khi sao chép, duyệt, hoặc cộng theo cột.

Lỗi số 4: Cho rằng mọi hàng đều cùng độ dài

Trong mảng răng cưa, các hàng có thể có độ dài khác nhau! Nếu bạn viết matrix[i][j], hãy đảm bảo rằng j < matrix[i].length.

Lỗi số 5: Lẫn lộn thứ tự chỉ số

Đôi khi nhầm lẫn rằng chỉ số thứ nhất là hàng rồi mới đến cột: matrix[row][column]. Không phải ngược lại!

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION