1. Mảng — vùng chứa các phần tử
Có lẽ bạn đã nghe rằng máy tính có thể xử lý khối lượng thông tin khổng lồ. Câu lệnh rẽ nhánh (if-else) và vòng lặp (for, while) tất nhiên có ích cho việc đó. Nhưng chừng đó là chưa đủ, vì dữ liệu cần được lưu trữ ở đâu đó để xử lý.
Trong Java, cũng như hầu hết các ngôn ngữ lập trình khác, có một công cụ tuyệt vời là mảng (Array). Chúng còn được gọi là bảng.
Mảng là một đối tượng đặc biệt cho phép lưu không chỉ một giá trị mà là nhiều giá trị.

Nếu trước đây chúng ta ví biến như một chiếc hộp (có thể chứa một giá trị nào đó), thì mảng giống như một chiếc hộp được chia thành nhiều ngăn ở bên trong. Mỗi ngăn trong “hộp-mảng” đều có số của nó. Việc đánh số, tất nhiên, bắt đầu từ 0...
Ta cũng có thể đưa ra một phép so sánh khác. Hãy so sánh một ngôi nhà riêng và một tòa nhà nhiều tầng. Nhà riêng do một gia đình ở, còn tòa nhà nhiều tầng được chia thành các căn hộ. Để gửi thư cho gia đình sống trong nhà riêng, bạn cần ghi địa chỉ duy nhất của ngôi nhà. Còn để gửi thư cho gia đình sống trong một căn hộ, bạn cần ghi địa chỉ duy nhất của tòa nhà và cả số căn hộ.
Vậy, biến-mảng giống như một tòa nhà nhiều tầng. Trong đó, bạn có thể lưu không chỉ một giá trị mà là nhiều giá trị. Biến như vậy có nhiều “căn hộ” (ô/phần tử), và bạn có thể truy cập từng ô thông qua số của nó (chỉ số).
Để làm điều đó, sau tên biến bạn cần ghi chỉ số của ô trong dấu ngoặc vuông. Rất đơn giản:
array[indeks] = znachenie;
Trong đó array là tên biến-mảng, indeks là số của ô trong mảng và znachenie là giá trị mà chúng ta muốn ghi vào ô được chỉ định.
Nhưng trước hết hãy xem cách tạo mảng.
2. Tạo mảng các phần tử trong Java

Giả sử chương trình của bạn cần lưu trữ 100 số nguyên. Rất có thể mảng sẽ phù hợp cho việc này. Vậy tạo mảng như thế nào?
Nếu chúng ta muốn lưu một số nguyên, kiểu int là phù hợp; còn nếu muốn lưu 100 số nguyên, có lẽ chúng ta cần một mảng kiểu int. Mã tạo mảng sẽ trông như sau:
int[] array = new int[100];
Hãy phân tích đoạn mã này.
Như bạn có thể đoán, bên trái dấu bằng là khai báo biến có tên array và kiểu int[]. Sau kiểu int là dấu ngoặc vuông, như để nhắc rằng “trong những chiếc hộp” thuộc kiểu này có thể chứa không chỉ một mà là nhiều giá trị.
Bên phải dấu bằng là “tạo đối tượng” (từ khóa new) gồm 100 phần tử (ô) kiểu int. Cũng không quá phức tạp.
Nếu, chẳng hạn, chúng ta muốn tạo một mảng gồm 20 ô để lưu số thực, mã tạo mảng sẽ như thế này:
double[] vals = new double[20];
Số lượng ô trong mảng được gọi là kích thước mảng hoặc độ dài mảng. Và nhờ khả năng lưu được nhiều giá trị, mảng còn được gọi là vùng chứa (container).
Sự thật quan trọng: kích thước của vùng chứa mảng không thể thay đổi sau khi đã tạo.
Bạn có thể tạo mảng mới, nhưng không thể thay đổi độ dài của vùng chứa đã được tạo.
3. Làm việc với các phần tử của mảng

Chúng ta đã biết cách tạo mảng, vậy làm việc với mảng như thế nào cho đúng?
Gần như giống hệt với biến thông thường, chỉ khác là sau tên biến-mảng bắt buộc phải chỉ ra số của ô (chỉ số) mà ta đang làm việc cùng.
Việc đánh số ô của mảng luôn bắt đầu từ 0. Nếu mảng có 10 phần tử, thì các số (chỉ số) là 0..9; nếu mảng có 200 phần tử, thì là 0..199. Và tương tự như vậy.
Ví dụ:
int[] a = new int[10]; // Tạo mảng gồm 10 phần tử kiểu int.
a[2] = 4; // Ghi giá trị 4 vào phần tử có chỉ số 2.
a[7] = 9; // Ghi giá trị 9 vào phần tử có chỉ số 7.
a[9] = a[2] + a[5]; // Ghi vào phần tử có chỉ số 9 tổng của các giá trị lưu ở
// các phần tử 2 (đang là 4) và 5 (đang là 0).
Đây là những gì sẽ được lưu trong bộ nhớ sau khi chạy đoạn mã trên:

Cột bên trái của mảng (màu xám) là các số (chỉ số) của ô. Trong các ô lưu các giá trị đã ghi: 4, 9 và 4. Ngay sau khi tạo, các ô của mảng được điền bằng số 0.
Quan trọng. Tất cả các ô của mảng có cùng một kiểu dữ liệu. Nếu chúng ta tạo mảng chuỗi String, thì trong các ô của nó chỉ có thể lưu chuỗi. Kiểu dữ liệu của mảng được xác định khi tạo. Về sau không thể thay đổi kiểu dữ liệu cũng như độ dài của mảng.
4. Mảng trong bộ nhớ
Các hình trong ví dụ trước hơi thiếu chính xác.
Khi tạo mảng (cũng như khi tạo chuỗi), trong bộ nhớ sẽ cấp phát hai khối nhớ: một khối để lưu chính mảng (vùng chứa), và khối thứ hai là cho biến lưu địa chỉ của mảng. Tình huống được làm rõ như trong hình dưới:

Màu xanh lá biểu diễn mảng gồm 10 phần tử kiểu int và biến kiểu int[], biến này lưu địa chỉ (tham chiếu) tới mảng kiểu int trong bộ nhớ.
Để so sánh, màu xanh lam biểu diễn biến thông thường kiểu int lưu giá trị 199.
Trông có vẻ giống cách lưu chuỗi trong bộ nhớ, đúng không?
Đúng vậy, tương tự với chuỗi. Và cũng như khi làm việc với chuỗi, “các biến kiểu mảng” có thể gán cho nhau:
int[] a = new int[10]; // Tạo mảng gồm 10 phần tử kiểu int.
a[2] = 4; // Ghi 4 vào phần tử có chỉ số 2.
a[7] = 9; // Ghi 9 vào phần tử có chỉ số 7.
int[] b = a; // Lưu vào biến b địa chỉ đang có trong biến a.
// Bây giờ a và b cùng trỏ tới một đối tượng-mảng trong bộ nhớ.
a[9] = b[2] + a[7]; // Ghi vào phần tử có chỉ số 9 của đối tượng-mảng
// tổng của các giá trị lưu ở phần tử 2 (4) và 7 (9).
Trong đó đối tượng-mảng vẫn ở nguyên vị trí của nó trong bộ nhớ, còn các biến a và b lưu cùng một địa chỉ (tham chiếu) tới cùng một đối tượng. Xem hình minh họa:

5. Làm việc với mảng chi tiết hơn
Có thể tạo mảng từ bất kỳ kiểu nào. Chỉ cần viết cặp ngoặc vuông sau tên kiểu. Dạng tổng quát khi tạo mảng như sau:
tip[] imya = new tip[kolichestvo];
Trong đó tip là kiểu của các phần tử (ô) chúng ta sẽ lưu trong mảng. Imya là tên biến mà qua đó ta truy cập mảng, còn kolichestvo là số lượng ô trong mảng.
Ví dụ trên thể hiện dạng chuẩn: vừa khai báo biến-mảng vừa tạo đối tượng-mảng. Trên thực tế, đây là hai thao tác độc lập. Có thể tạo biến-mảng và đối tượng-mảng riêng rẽ:
tip[] imya;
imya = new tip[kolichestvo];
Và một điểm quan trọng nữa
Cả chỉ số của mảng lẫn số lượng phần tử của mảng đều có thể là biến, thậm chí là các biểu thức đầy đủ.
Ví dụ:
int n = 100;
int[] a = new int[n]; // Tạo mảng gồm n phần tử
int n = 100;
int[] a = new int[n * 2 + 3]; // Tạo mảng có 203 phần tử
int n = 100;
int[] a = new int[n];
a[n-1] = 2; // a[99] = 2;
a[n-2] = 3; // a[98] = 3;
a[n/5] = a[n-1] + a[n-2] // a[20] = a[99] + a[98];
Vượt quá giới hạn của mảng
Nhân tiện, lưu ý rằng nếu bạn cố truy cập một ô theo chỉ số không tồn tại trong mảng (trong trường hợp này là mọi số nguyên ngoài phạm vi 0..99), chương trình sẽ kết thúc bất thường với lỗi ArrayIndexOutOfBoundsException — chỉ số vượt ra ngoài giới hạn của mảng.
6. Độ dài mảng
Như ta đã thấy ở ví dụ trước, có thể tạo biến kiểu mảng riêng, rồi sau đó ở đâu đó trong mã mới gán cho nó giá trị (tham chiếu tới đối tượng-mảng). Thậm chí có thể làm như sau:
int[] array; // Tạo biến-mảng kiểu int[]
if (a < 10) // Nếu biến a nhỏ hơn 10,
array = new int[10]; // thì tạo mảng gồm 10 phần tử.
else // Ngược lại
array = new int[20]; // tạo mảng gồm 20 phần tử
Vậy sau đó làm việc với mảng như thế nào? Làm sao biết nó có bao nhiêu phần tử?
Để làm điều đó, mảng có một thuộc tính đặc biệt — length. Có thể lấy độ dài mảng bằng biểu thức sau:
array.length;
Trong đó array là tên biến-mảng, còn length là tên thuộc tính của đối tượng-mảng. Không thể thay đổi giá trị trong thuộc tính length: có thể gán length sang biến khác, nhưng không thể gán giá trị cho chính nó (chương trình sẽ không biên dịch).
Đây là cách có thể tiếp tục ví dụ trước:
int[] array; // Tạo biến-mảng kiểu int[]
if (a < 10) // Nếu biến a nhỏ hơn 10,
array = new int[10]; // thì tạo mảng gồm 10 phần tử.
else // Ngược lại
array = new int[20]; // tạo mảng gồm 20 phần tử
for (int i = 0; i < array.length; i++) // Duyệt qua tất cả các phần tử của mảng: từ 0 đến array.length — 1
{
System.out.println(array[i]);
}
7. Một vài sự thật về mảng trong Java
Hãy tổng hợp lại những điều đã biết về mảng:
- Sự thật 1. Mảng gồm nhiều ô (phần tử).
- Sự thật 2. Truy cập một ô cụ thể thông qua chỉ số của nó.
- Sự thật 3. Tất cả các ô có cùng một kiểu.
- Sự thật 4. Giá trị khởi tạo cho tất cả các ô — 0 và null (nếu ô lưu địa chỉ), false (cho kiểu boolean).
- Sự thật 5. String[] list — chỉ là khai báo biến: bản thân vùng chứa (đối tượng-mảng) chưa được tạo. Để có thể làm việc, cần tạo mảng (vùng chứa) và gán nó vào biến này, rồi mới sử dụng. Xem ví dụ dưới.
- Sự thật 6. Khi chúng ta tạo đối tượng-mảng (vùng chứa), cần chỉ ra độ dài — có bao nhiêu ô trong đó. Làm bằng lệnh dạng: new TypeName[n];
- Sự thật 7. Có thể biết độ dài mảng qua thuộc tính .length.
- Sự thật 8. Sau khi tạo mảng, không thể thay đổi kiểu các phần tử của nó cũng như số lượng phần tử.
String s; // s bằng null
String[] list; // list bằng null
list = new String[10]; // Biến list lưu tham chiếu tới đối tượng — mảng chuỗi gồm 10 phần tử.
int n = list.length; // n bằng 10
list = new String[0]; // Bây giờ list chứa mảng có 0 phần tử.
// Có mảng, nhưng không thể lưu phần tử nào.
list = null;
System.out.println(list[1]); // Sẽ phát sinh lỗi chương trình — chương trình kết thúc bất thường.
// list chứa tham chiếu rỗng — null
list = new String[10];
System.out.println(list[10]); // Sẽ phát sinh lỗi — vượt quá giới hạn của mảng.
// Nếu list có 10 phần tử/ô, thì các chỉ số hợp lệ: 0 1 2 3 4 5 6 7 8 9 — tổng cộng 10 số.
GO TO FULL VERSION