Mảng là danh sách có kích thước cố định
Mảng (array) trong Java chứa một số lượng cố định các giá trị cùng kiểu, xếp theo thứ tự và truy cập theo vị trí. "Cố định" là từ khóa quan trọng: khi đã tạo mảng, độ dài của nó không bao giờ thay đổi. Còn "cùng kiểu": một int[] chỉ chứa giá trị int, một String[] chỉ chứa chuỗi. Sự cứng nhắc đó là cái giá để mảng chạy nhanh và gọn trong bộ nhớ.
Bạn khai báo kiểu mảng bằng cách đặt [] sau kiểu phần tử, rồi gán một giá trị literal hoặc cấp phát bằng new:
Dấu ngoặc vuông có thể đặt sau kiểu (int[] scores) hoặc sau tên (int scores[]). Cả hai đều biên dịch được, nhưng int[] scores mới là phong cách chuẩn của Java: đặt [] cạnh kiểu, đúng chỗ của nó.
Tạo mảng bằng new
Khi chưa biết giá trị nhưng đã biết kích thước, hãy dùng new. Lệnh này cấp phát các ô và điền giá trị mặc định của kiểu đó:
Giá trị mặc định không phải dữ liệu rác ngẫu nhiên: Java khởi tạo mọi phần tử về giá trị không. Kiểu số bắt đầu từ 0 (hoặc 0.0), boolean bắt đầu là false, còn kiểu tham chiếu như String bắt đầu là null. Giá trị null đó đáng nhớ: một String[] vừa cấp phát chứa toàn null, và gọi phương thức trên một phần tử như vậy sẽ ném ra NullPointerException.
Chỉ số bắt đầu từ 0
Bạn đọc và ghi phần tử theo chỉ số, đếm từ 0. Chỉ số hợp lệ cuối cùng luôn là length - 1:
Khác với mảng trong một số ngôn ngữ kịch bản, truy cập ra ngoài phạm vi không lặng lẽ trả về null hay undefined: nó ném ra ngoại lệ. Thử đọc một chỉ số không tồn tại:
Chương trình đó dừng với lỗi:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3
Đây là một trong những lỗi phổ biến nhất của người mới học. Cách sửa gần như luôn là chữa lỗi lệch một đơn vị trong điều kiện vòng lặp: dùng <= trong khi đúng ra phải là <.
length là trường, không phải phương thức
Mọi mảng đều mang kích thước của nó trong trường length. Lưu ý: không có dấu ngoặc.
Đây là cái bẫy ai cũng dính ít nhất một lần: mảng dùng .length (một trường), còn String dùng .length() (một phương thức). Viết nums.length() trên mảng là lỗi biên dịch, và viết text.length trên chuỗi cũng vậy. Cùng một từ, khác quy tắc: của mảng thì không có dấu ngoặc.
Duyệt mảng
Bạn đã học vòng lặp for-each ở trang trước, và đó là cách gọn nhất để đọc mọi phần tử khi không cần chỉ số:
Khi cần chỉ số, để in số thứ tự, sửa phần tử tại chỗ hoặc duyệt ngược, hãy dùng vòng lặp (loop) for có chỉ số truyền thống với length làm giới hạn:
Điều kiện là i < names.length, không phải <=. Dùng <= sẽ chạy quá một bước và ném ra ngoại lệ vượt phạm vi như ở phần trên. Quy tắc nhanh: dùng for-each khi chỉ đọc giá trị, dùng for có chỉ số khi cần vị trí hoặc muốn ghi ngược lại vào mảng.
Mảng nhiều chiều
Mảng 2 chiều thực chất là mảng của các mảng: hãy hình dung nó như một lưới gồm hàng và cột. Bạn thêm một cặp ngoặc vuông thứ hai:
grid[row][column] chọn một ô. Vì mỗi hàng là một mảng riêng, grid.length là số hàng còn grid[0].length là số cột của hàng đầu tiên. Các hàng thậm chí không cần dài bằng nhau (gọi là mảng răng cưa, jagged array), dù hầu hết các lưới đều là hình chữ nhật.
Lớp tiện ích Arrays
Bản thân mảng gần như không có phương thức nào: bạn không thể sắp xếp hay in trực tiếp một mảng theo cách hữu ích. Java cung cấp lớp tiện ích java.util.Arrays chứa đầy các phương thức static cho đúng những việc này:
Vài điều nên nắm ở đây. Arrays.toString(arr) là cách đúng để in một mảng; in trực tiếp sẽ cho ra một chuỗi vô nghĩa như [I@1b6d3586. Arrays.sort sắp xếp tại chỗ. Arrays.copyOf là cách bạn "đổi kích thước": nó trả về một mảng mới có độ dài bạn yêu cầu, phần thừa được điền giá trị mặc định (hai ô thêm ở trên là 0). Còn Arrays.binarySearch chỉ chạy đúng trên mảng đã được sắp xếp.
Mảng và ArrayList
Dùng mảng thường khi kích thước đã biết trước và không thay đổi: một tập cố định các ngày trong tuần, một bàn cờ, dữ liệu điểm ảnh. Mảng nhanh và nhẹ, nhưng độ dài cố định là một giới hạn thật sự: bạn không thể thêm phần tử thứ bảy vào mảng có độ dài 6 mà không tạo hẳn một mảng mới.
Ngay khi cần thêm và xóa phần tử thoải mái, mảng trở nên bất tiện. Đó là lúc dùng ArrayList: một danh sách co giãn, tự lớn lên và lo việc sao chép cho bạn.
Tiếp theo: ArrayList
Khi bạn không biết mình sẽ có bao nhiêu phần tử, hoặc số lượng đó thay đổi trong lúc chương trình chạy, một mảng có độ dài cố định sẽ gây cản trở. ArrayList là câu trả lời của Java: một danh sách tự lớn lên và thu nhỏ khi cần, với các phương thức add và remove thay vì phải xoay xở với chỉ số. Đó là nội dung tiếp theo.
Câu hỏi thường gặp
Làm sao khai báo và khởi tạo mảng trong Java?
Khai báo kiểu với dấu ngoặc vuông và gán một giá trị literal: int[] nums = {1, 2, 3};. Để tạo một mảng rỗng có kích thước cố định, hãy dùng new: int[] nums = new int[5]; cho bạn 5 ô, mỗi ô chứa giá trị mặc định (0 với int, null với đối tượng).
Làm sao lấy độ dài của mảng trong Java?
Dùng trường length (không có dấu ngoặc): nums.length. Đây là một trường, không phải phương thức, nên nums.length() là lỗi biên dịch. Lưu ý String dùng .length() có dấu ngoặc, còn mảng dùng .length không có ngoặc, một chỗ nhầm lẫn kinh điển.
Mảng trong Java có đổi kích thước được sau khi tạo không?
Không. Mảng Java có độ dài cố định được đặt khi tạo và bạn không thể tăng hay giảm nó. Nếu cần một danh sách co giãn được, hãy dùng ArrayList. Để "đổi kích thước" một mảng, bạn phải tạo một mảng mới lớn hơn rồi sao chép các phần tử sang.
ArrayIndexOutOfBoundsException trong Java là gì?
Đó là lỗi lúc chạy xảy ra khi bạn truy cập một chỉ số không tồn tại: nhỏ hơn 0 hoặc >= array.length. Khác với một số ngôn ngữ, Java không trả về giá trị mặc định hay null khi truy cập ngoài phạm vi; nó ném ra ngoại lệ. Chỉ số hợp lệ luôn chạy từ 0 đến length - 1.