Mảng — Array 1D & 2D¶
1. Khái niệm¶
Mảng là cấu trúc dữ liệu lưu một dãy các phần tử cùng kiểu, kích thước cố định, được đánh index từ 0.
Ba đặc điểm cốt lõi:
- Fixed-size — kích thước xác định khi tạo, không thể thay đổi sau đó
- Zero-indexed — phần tử đầu ở index
0, phần tử cuối ở indexlength - 1 - Heap-allocated — biến trên Stack chỉ lưu reference (địa chỉ), dữ liệu thật nằm trên Heap
2. Tại sao quan trọng¶
Mảng là nền tảng của mọi cấu trúc dữ liệu trong Java:
ArrayListbên trong dùng mộtObject[]để lưu dữ liệuHashMapbên trong là một mảng các bucket- Thuật toán sắp xếp, tìm kiếm đều bắt đầu từ mảng
- Interview thường xuyên: two-pointer, sliding window, prefix sum — đều trên mảng
3. Khai báo và khởi tạo¶
Ba cách khai báo mảng 1D¶
// Cách 1 — cấp phát, chưa gán giá trị (dùng default values)
int[] scores = new int[5]; // [0, 0, 0, 0, 0]
// Cách 2 — khai báo + khởi tạo ngay (phổ biến nhất)
int[] scores = {85, 92, 78, 95, 88};
// Cách 3 — new + initializer (dùng khi truyền thẳng vào method)
printArray(new int[]{85, 92, 78, 95, 88});
Giá trị mặc định khi dùng new¶
| Kiểu | Giá trị mặc định |
|---|---|
int, long, short, byte |
0 |
double, float |
0.0 |
boolean |
false |
char |
' ' |
Object (String, ...) |
null |
int[] arr = new int[3];
System.out.println(arr[0]); // 0
String[] names = new String[3];
System.out.println(names[0]); // null
Truy cập và thuộc tính¶
int[] scores = {85, 92, 78, 95, 88};
System.out.println(scores[0]); // 85 — phần tử đầu
System.out.println(scores[4]); // 88 — phần tử cuối
System.out.println(scores.length); // 5 — field, không có dấu ()
scores[2] = 80; // gán lại giá trị
.length là field, không phải method
arr.length — không có dấu (). Khác với String.length() hay List.size(). Hay nhầm khi mới học.
4. Duyệt mảng¶
5. Các thao tác phổ biến¶
Tổng và trung bình¶
int[] scores = {85, 92, 78, 95, 88};
int sum = 0;
for (int s : scores) sum += s;
double avg = (double) sum / scores.length;
System.out.println("Tổng: " + sum); // 438
System.out.println("Trung bình: " + avg); // 87.6
Tìm min / max¶
int[] scores = {85, 92, 78, 95, 88};
int min = scores[0], max = scores[0];
for (int i = 1; i < scores.length; i++) {
if (scores[i] < min) min = scores[i];
if (scores[i] > max) max = scores[i];
}
System.out.println("Min: " + min + ", Max: " + max); // Min: 78, Max: 95
Sao chép mảng¶
int[] original = {1, 2, 3, 4, 5};
// Arrays.copyOf — gọn nhất
int[] copy1 = Arrays.copyOf(original, original.length);
// Arrays.copyOfRange — copy một đoạn [fromIndex, toIndex)
int[] copy2 = Arrays.copyOfRange(original, 1, 4); // [2, 3, 4]
// System.arraycopy — nhanh nhất, kiểm soát tuyệt đối
int[] copy3 = new int[original.length];
System.arraycopy(original, 0, copy3, 0, original.length);
Không copy mảng bằng =
6. Mảng 2D¶
Mảng 2D là mảng của các mảng — dùng để biểu diễn ma trận, bảng, lưới ô vuông.
// Khai báo + cấp phát
int[][] matrix = new int[3][4]; // 3 hàng, 4 cột, mặc định 0
// Khai báo + khởi tạo
int[][] grid = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// Truy cập: [hàng][cột]
System.out.println(grid[1][2]); // 6
System.out.println(grid.length); // 3 — số hàng
System.out.println(grid[0].length); // 3 — số cột của hàng 0
Duyệt mảng 2D¶
Jagged array — hàng dài ngắn khác nhau
Java cho phép từng hàng trong mảng 2D có độ dài khác nhau:
int[][] triangle = new int[4][];
for (int i = 0; i < triangle.length; i++) {
triangle[i] = new int[i + 1];
}
// hàng 0: [0]
// hàng 1: [0, 0]
// hàng 2: [0, 0, 0]
// hàng 3: [0, 0, 0, 0]
Điều này khác với C/C++ — trong Java, mảng 2D không phải vùng nhớ liên tục mà là mảng các reference.
7. java.util.Arrays¶
Arrays là utility class chứa các phương thức xử lý mảng hay dùng nhất — cần import java.util.Arrays.
int[] arr = {5, 2, 8, 1, 9, 3};
// Sắp xếp tại chỗ
Arrays.sort(arr);
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 5, 8, 9]
// Tìm nhị phân — mảng phải đã sort
int idx = Arrays.binarySearch(arr, 5); // 3
// Điền giá trị
int[] filled = new int[5];
Arrays.fill(filled, 7); // [7, 7, 7, 7, 7]
// Sao chép
int[] copy = Arrays.copyOf(arr, 4); // [1, 2, 3, 5]
int[] range = Arrays.copyOfRange(arr, 2, 5); // [3, 5, 8]
// So sánh nội dung
int[] a = {1, 2, 3}, b = {1, 2, 3};
System.out.println(Arrays.equals(a, b)); // true
System.out.println(a == b); // false — địa chỉ khác
// Mảng 2D
int[][] x = {{1, 2}, {3, 4}};
int[][] y = {{1, 2}, {3, 4}};
System.out.println(Arrays.deepEquals(x, y)); // true
System.out.println(Arrays.deepToString(x)); // [[1, 2], [3, 4]]
8. Array vs ArrayList¶
int[] / String[] |
ArrayList<Integer> |
|
|---|---|---|
| Kích thước | Cố định | Động, tự mở rộng |
| Kiểu phần tử | Primitive + Object | Object only (autoboxing) |
| Hiệu năng | Cao hơn | Có overhead của boxed type |
| API | Ít (Arrays.*) |
Phong phú (add, remove, contains...) |
| Khi dùng | Kích thước biết trước, cần hiệu năng | Kích thước không biết trước, cần thêm/xóa |
Nguyên tắc chọn
Biết trước số phần tử và không cần thêm/xóa → dùng array. Còn lại → ArrayList. Phase 02 sẽ đi sâu vào toàn bộ Java Collections.
9. Code ví dụ¶
Verified
Bản đầy đủ có thể compile: ArraysDemo.java
- Cast
sum(arr)sangdoubletrước khi chia — không cast thì phép chia giữa haiintsẽ cắt phần thập phân, trả về87thay vì87.6. twoSumlà bài LeetCode #1 — bài đầu tiên hầu hết mọi người làm. Hiểu array và vòng lặp lồng nhau là đủ để giải O(n²). Hash map cho O(n) — Phase 02 sẽ bàn.
10. Lỗi thường gặp¶
Lỗi 1 — ArrayIndexOutOfBoundsException¶
int[] arr = {1, 2, 3}; // length = 3, index hợp lệ: 0, 1, 2
System.out.println(arr[3]); // ❌ ArrayIndexOutOfBoundsException: index 3 out of bounds for length 3
System.out.println(arr[arr.length - 1]); // ✅ phần tử cuối luôn ở length - 1
Lỗi 2 — NullPointerException với mảng Object¶
String[] names = new String[3]; // [null, null, null]
System.out.println(names[0].length()); // ❌ NullPointerException
if (names[0] != null) { // ✅ kiểm tra trước khi dùng
System.out.println(names[0].length());
}
Lỗi 3 — So sánh mảng bằng ==¶
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println(a == b); // false — so sánh địa chỉ bộ nhớ
System.out.println(Arrays.equals(a, b)); // true ✅
int[][] x = {{1, 2}, {3, 4}};
int[][] y = {{1, 2}, {3, 4}};
System.out.println(Arrays.deepEquals(x, y)); // true ✅ — bắt buộc dùng deepEquals cho 2D
Lỗi 4 — Gán = tưởng là copy¶
int[] original = {1, 2, 3};
int[] alias = original; // ❌ cùng reference
int[] copy = Arrays.copyOf(original, 3); // ✅ copy thật sự
alias[0] = 99;
System.out.println(original[0]); // 99 — bị ảnh hưởng!
copy[0] = 77;
System.out.println(original[0]); // vẫn 99 — an toàn ✅
Lỗi 5 — In mảng trực tiếp¶
int[] arr = {1, 2, 3};
System.out.println(arr); // ❌ [I@6d06d69c — địa chỉ bộ nhớ
System.out.println(Arrays.toString(arr)); // ✅ [1, 2, 3]
int[][] m = {{1, 2}, {3, 4}};
System.out.println(Arrays.toString(m)); // ❌ [[I@..., [I@...]
System.out.println(Arrays.deepToString(m)); // ✅ [[1, 2], [3, 4]]
11. Câu hỏi phỏng vấn¶
Q1: Sự khác nhau giữa Array và ArrayList?
Arraykích thước cố định, chứa được primitive, hiệu năng cao hơn.ArrayListkích thước động, chỉ chứa Object (autoboxing với primitive), API phong phú hơn. Chọn array khi kích thước biết trước và không cần thêm/xóa; chọnArrayListkhi cần linh hoạt.
Q2: Làm sao copy mảng đúng cách? Shallow copy là gì?
Dùng
Arrays.copyOf(),Arrays.copyOfRange(), hoặcSystem.arraycopy(). Gánb = akhông phải copy — chỉ tạo thêm reference trỏ vào cùng mảng. Lưu ý: đây đều là shallow copy — nếu mảng chứa Object, chỉ reference được copy, object bên trong không được duplicate.
Q3: Giá trị mặc định khi tạo mảng bằng new là gì?
Numeric →
0/0.0.boolean→false.char→' '. Object →null. Java luôn khởi tạo phần tử mảng khi cấp phát, không bao giờ có "garbage value" như C/C++.
Q4: Tại sao không dùng Arrays.equals() cho mảng 2D?
Arrays.equals()chỉ so sánh một lớp — nó so sánh các phần tử của mảng ngoài, mà mỗi phần tử là mộtint[](reference). Hai reference khác nhau dù trỏ đến mảng cùng nội dung vẫn trả vềfalse. Phải dùngArrays.deepEquals()để so sánh đệ quy.
Q5: Arrays.binarySearch() yêu cầu điều kiện gì?
Mảng phải được sắp xếp tăng dần trước khi gọi. Nếu chưa sort, kết quả trả về là undefined. Thông thường kết hợp
Arrays.sort()trước rồi mớibinarySearch().
12. Tài liệu tham khảo¶
| Tài liệu | Nội dung |
|---|---|
| JLS §10 — Arrays | Đặc tả chính thức về mảng trong Java |
| java.util.Arrays Javadoc | API reference đầy đủ |
| Oracle Tutorial — Arrays | Hướng dẫn chính thức |
| Effective Java — Joshua Bloch | Item 28: Prefer lists to arrays (lý do tại sao generics và array không hợp nhau) |