Mục lục bài họcĐang ở d04-b2
ArrayList and Safe Removal
The six methods the exam tests
An ArrayList<E> grows and shrinks at run time. The type parameter must be an object type, so a list of integers is declared ArrayList<Integer> list = new ArrayList<Integer>(); — never ArrayList<int>.
int size() number of elements E get(int i) element at index i E set(int i, E x) replaces element i, RETURNS the old one boolean add(E x) appends at the end, returns true void add(int i, E x) inserts at i, shifts everything right E remove(int i) removes element i, RETURNS it, shifts left
Two of these are quietly important. set and remove return the element that used to be there, which lets a question hide a value in an expression such as System.out.println(list.remove(0));.
Autoboxing
list.add(5) works on an ArrayList<Integer> because Java boxes the int into an Integer. Going the other way, int n = list.get(0); unboxes automatically.
Sáu phương thức đề thi hỏi đi hỏi lại: size(), get(i), set(i, x), add(x), add(i, x), remove(i). Kiểu trong ngoặc nhọn bắt buộc là kiểu đối tượng: viết ArrayList<Integer> chứ không bao giờ ArrayList<int>. Với chuỗi thì là ArrayList<String>.
Hai phương thức trả về giá trị cũ. set(i, x) và remove(i) đều trả về đúng phần tử vừa bị thay hoặc vừa bị lấy đi. Đề rất hay giấu một giá trị vào đó, kiểu System.out.println(list.remove(0)); — người đọc lướt tưởng dòng đó chỉ xoá, thực ra nó còn in ra thứ vừa xoá.
Hai phương thức làm dịch chỗ. add(i, x) đẩy mọi phần tử từ vị trí i trở đi sang phải một bậc; remove(i) kéo mọi phần tử phía sau tụt xuống trái một bậc. Sau mỗi lần gọi, chỉ số của các phần tử phía sau đã đổi và size() cũng đổi. Đây chính là nguồn gốc của bẫy lớn nhất chương này.
Đóng hộp tự động. list.add(5) chạy được trên ArrayList<Integer> vì Java tự bọc int thành Integer; chiều ngược lại int n = list.get(0); cũng tự mở hộp.
i++ đưa nó thẳng sang bậc 2. Phần tử vừa tụt xuống không bao giờ được xét. Hậu quả rất đặc trưng: dãy có hai giá trị cần xoá nằm cạnh nhau thì cái thứ hai sống sót, còn dãy không có giá trị nào kề nhau thì chương trình chạy đúng — nên lỗi này qua được phần lớn ca thử viết vội.Cho ArrayList<Integer> list. Xoá mọi phần tử bằng 4. Viết hai lời giải đúng và chỉ ra chỗ khác nhau với bản sai.
Bản sai — duyệt xuôi và vẫn tăng i sau khi xoá:
for (int i = 0; i < list.size(); i++) {
if (list.get(i) == 4) { list.remove(i); } // bỏ sót
}Lối 1 — duyệt xuôi, chỉ tăng chỉ số khi KHÔNG xoá:
int i = 0;
while (i < list.size()) {
if (list.get(i) == 4) {
list.remove(i); // không tăng i
} else {
i++;
}
}Lối 2 — duyệt ngược từ cuối về đầu:
for (int i = list.size() - 1; i >= 0; i--) {
if (list.get(i) == 4) { list.remove(i); }
}Lối 2 an toàn vì việc dồn chỗ chỉ ảnh hưởng tới các bậc phía sau vị trí vừa xoá, mà những bậc đó thì vòng lặp ngược đã đi qua rồi. Trong phòng thi lối 2 ngắn hơn và ít chỗ sai hơn.
Với [4, 4, 7, 4, 9]: bản sai cho [4, 7, 9], cả hai lối đúng đều cho [7, 9].
remove thì cấm dùng for-each (nó ném ConcurrentModificationException) và cấm dùng for xuôi mặc định; hai, chọn một trong hai lối đã học — duyệt ngược, hoặc while chỉ tăng chỉ số ở nhánh else. Tự thử ca [4, 4] trước khi nộp là bắt được ngay.set và remove trả về giá trị cũ; add(i, x) và remove(i) làm dịch chỗ và đổi size(). Muốn xoá khi đang duyệt thì duyệt ngược, hoặc chỉ tăng chỉ số khi không xoá.Đọc xong rồi — làm thử ngay
Bài tập của chương Unit 4 — Data Collections gồm 14 câu trắc nghiệm và 8 đề tự luận. Đáp án hiện ngay khi chọn, miễn phí.