CodeGym/Blog Java/Ngẫu nhiên/Xóa một phần tử khỏi ArrayList

Xóa một phần tử khỏi ArrayList

Xuất bản trong nhóm
CHÀO! Trong bài học trước, chúng ta đã làm quen với ArrayListlớp này và học cách thực hiện các thao tác phổ biến nhất với lớp này. Ngoài ra, chúng tôi đã chỉ ra một số điểm khác biệt giữa mảng an toàn ArrayListvà mảng thông thường. Nhưng chúng tôi đã bỏ qua một chủ đề, cụ thể là cách xóa các thành phần khỏi tệp ArrayList. Chúng ta sẽ thảo luận về điều đó ngay bây giờ. Xóa phần tử khỏi ArrayList - 1Chúng tôi đã đề cập rằng việc xóa các phần tử khỏi một mảng thông thường không thuận tiện lắm. Vì chúng tôi không thể xóa chính phần tử đó, chúng tôi chỉ có thể "bỏ qua" (đặt thành null) giá trị của nó:
public class Cat {

   private String name;

   public Cat(String name) {
       this.name = name;
   }

   public static void main(String[] args) {

       Cat[] cats = new Cat[3];
       cats[0] = new Cat("Thomas");
       cats[1] = new Cat("Behemoth");
       cats[2] = new Cat("Lionel Messi");

       cats[1] = null;

       System.out.println(Arrays.toString(cats));
   }


@Override
   public String toString() {
       return "Cat{" +
               "name='" + name + '\'' +
               '}';
   }
}
Kết quả: [Cat{name='Thomas'}, null, Cat{name='Lionel Messi'}] Nhưng việc đặt một phần tử mảng thành null sẽ để lại một "lỗ hổng". Chúng tôi chưa xóa vị trí trong mảng, chỉ xóa nội dung của nó. Hãy tưởng tượng điều gì sẽ xảy ra nếu chúng ta có một dãy gồm 50 con mèo và loại bỏ 17 con theo cách này. Chúng tôi sẽ có một mảng với 17 lỗ. Chỉ cần cố gắng theo dõi chúng! Sẽ không thực tế nếu bạn nhớ số lượng ô trống nơi bạn có thể viết các giá trị mới. Nếu mắc một lỗi, bạn sẽ ghi đè lên tham chiếu đối tượng mà bạn muốn. Tất nhiên, có một cách để làm điều này cẩn thận hơn một chút: sau khi loại bỏ một phần tử, hãy di chuyển các phần tử lên phía trước mảng để đặt "lỗ" ở cuối:
public static void main(String[] args) {

   Cat[] cats = new Cat[4];
   cats[0] = new Cat("Thomas");
   cats[1] = new Cat("Behemoth");
   cats[2] = new Cat("Lionel Messi");
   cats[2] = new Cat("Fluffy");

   cats[1] = null;

   for (int i = 2; i < cats.length-1; i++) {
       cats [i-1] = cats [i];// Move the elements to the front of the array, so the empty position is at the end
   }

   System.out.println(Arrays.toString(cats));
}
Đầu ra: [Cat{name='Thomas'}, Cat{name='Fluffy'}, Cat{name='Fluffy'}, null] Điều này có vẻ tốt hơn nhưng khó có thể gọi là giải pháp hiệu quả. Nếu không vì lý do nào khác ngoài thực tế là chúng ta phải viết mã này mỗi khi chúng ta xóa một phần tử khỏi một mảng! Đây là một lựa chọn tồi. Chúng ta có thể đi theo một cách khác và tạo một phương thức riêng:
public void deleteCat(Cat[] cats, int indexToDelete) {
   //...delete the cat corresponding to the index and move the elements
}
Nhưng điều này cũng ít được sử dụng: phương pháp này chỉ có thể hoạt động với Catcác đối tượng chứ không thể hoạt động với các loại khác. Nói cách khác, nếu một chương trình có 100 lớp khác mà chúng ta muốn sử dụng với mảng, thì chúng ta sẽ phải viết cùng một phương thức với cùng một logic chính xác trong mỗi lớp. Đây là một thảm họa tổng thể -_- Nhưng ArrayListlớp giải quyết vấn đề này! Nó thực hiện một phương thức đặc biệt để loại bỏ các phần tử:remove()
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);
   System.out.println(cats.toString());

   cats.remove(1);

   System.out.println(cats.toString());
}
Chúng tôi chuyển chỉ mục của đối tượng cho phương thức, phương thức này sẽ xóa nó (giống như trong một mảng). Phương pháp này remove()có hai tính năng đặc biệt. Đầu tiên, nó không để lại "lỗ hổng". Nó đã thực hiện logic cần thiết để dịch chuyển các phần tử khi một phần tử bị xóa khỏi giữa, mà chúng tôi đã tự viết trước đó. Nhìn vào đầu ra từ đoạn mã trước:
[Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]

[Cat{name='Thomas'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]
Chúng tôi loại bỏ một con mèo ở giữa và những con còn lại được di chuyển để không còn chỗ trống. Thứ hai , nó có thể xóa các đối tượng không chỉ theo chỉ mục (giống như một mảng bình thường), mà còn theo tham chiếu :
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);
   System.out.println(cats.toString());

   cats.remove(lionel);

   System.out.println(cats.toString());
}
Đầu ra: [Mèo{name='Thomas'}, Mèo{name='Behemoth'}, Mèo{name='Lionel Messi'}, Mèo{name='Fluffy'}] [Mèo{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] Điều này có thể rất thuận tiện nếu bạn không muốn luôn theo dõi chỉ mục của đối tượng mong muốn. Có vẻ như chúng tôi đã tìm ra cách xóa thông thường. Bây giờ hãy tưởng tượng tình huống này: chúng tôi muốn lặp lại danh sách của mình và xóa một con mèo có tên cụ thể . Để làm điều này, chúng ta sẽ sử dụng một forvòng lặp nhanh (còn được gọi là vòng lặp for-each), mà chúng ta đã được giới thiệu trong các bài học của Rishi:
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);

   for (Cat cat: cats) {

       if (cat.name.equals("Behemoth")) {
           cats.remove(cat);
       }
   }

   System.out.println(cats);
}
Mã này trông hoàn toàn hợp lý. Nhưng kết quả có thể là một bất ngờ lớn: Ngoại lệ trong luồng "chính" java.util.ConcurrentModificationException tại java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) tại java.util.ArrayList$Itr.next(ArrayList. java:831) tại Cat.main(Cat.java:25) Đã xảy ra một số lỗi và không rõ tại sao lại xảy ra. Quá trình này liên quan đến một số sắc thái phải được giải quyết. Đây là quy tắc chung mà bạn cần nhớ: Bạn không thể đồng thời lặp qua một bộ sưu tập và thay đổi các thành phần của nó. Và chúng tôi muốn nói đến bất kỳ loại thay đổi nào, không chỉ đơn thuần là loại bỏ. Nếu bạn thay thế thao tác xóa mèo bằng cố gắng chèn mèo mới, kết quả sẽ giống nhau:
for (Cat cat: cats) {

   cats.add(new Cat("Salem Saberhagen"));
}

System.out.println(cats);
Ngoại lệ trong chuỗi "chính" java.util.ConcurrentModificationException tại java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) tại java.util.ArrayList$Itr.next(ArrayList.java:831) tại Cat.main( Cat.java:25) Chúng tôi đã thay đổi thao tác này sang thao tác khác, nhưng kết quả không thay đổi: chúng tôi nhận được cùng một ConcurrentModificationException . Nó xảy ra chính xác khi chúng tôi cố gắng phá vỡ quy tắc trên bằng cách thay đổi danh sách trong khi lặp lại nó. Trong Java, chúng ta cần một đối tượng đặc biệt được gọi là trình vòng lặp (Iteratorlớp) để xóa các mục trong khi lặp qua một bộ sưu tập. LớpIteratorchịu trách nhiệm lặp lại danh sách các phần tử một cách an toàn. Nó khá đơn giản, vì nó chỉ có 3 phương pháp:
  • hasNext()- trả về đúng hoặc sai, tùy thuộc vào việc có mục tiếp theo trong danh sách hay chúng ta đã đến mục cuối cùng.
  • next()- trả về mục tiếp theo trong danh sách
  • remove()- xóa một mục khỏi danh sách
Như bạn có thể thấy, iterator được thiết kế riêng cho nhu cầu của chúng ta, đồng thời không có gì phức tạp về nó. Giả sử chúng tôi muốn kiểm tra xem có phần tử tiếp theo trong danh sách của chúng tôi hay không và hiển thị phần tử đó nếu có:
Iterator<Cat> catIterator = cats.iterator();// Create an iterator
while(catIterator.hasNext()) {// As long as there are elements in the list

   Cat nextCat = catIterator.next();// Get the next element
   System.out.println(nextCat);// Display it
}
Đầu ra: Cat{name='Thomas'} Cat{name='Behemoth'} Cat{name='Lionel Messi'} Cat{name='Fluffy'} Như bạn thấy, đã triển khai một phương thức đặc biệt để tạo ArrayListmột trình lặp: iterator(). Ngoài ra, lưu ý rằng khi chúng ta tạo một trình vòng lặp, chúng ta chỉ định lớp đối tượng mà nó sẽ làm việc với ( <Cat>). Điểm mấu chốt là một iterator dễ dàng xử lý tác vụ ban đầu của chúng ta. Ví dụ: xóa con mèo có tên "Lionel Messi":
Iterator<Cat> catIterator = cats.iterator();// Create an iterator
while(catIterator.hasNext()) {// As long as there are elements in the list

   Cat nextCat = catIterator.next();// Get the next element
   if (nextCat.name.equals("Lionel Messi")) {
       catIterator.remove();// Delete the cat with the specified name
   }
}

System.out.println(cats);
Đầu ra: [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] Bạn có thể nhận thấy rằng chúng tôi đã không chỉ định chỉ mục hoặc tên trong remove()phương thức của trình vòng lặp ! Trình vòng lặp thông minh hơn vẻ ngoài của nó: remove()loại bỏ phần tử cuối cùng được trả về bởi trình vòng lặp. Như bạn có thể thấy, nó đã làm đúng những gì chúng ta muốn :) Về nguyên tắc, đây là mọi thứ bạn cần biết về việc xóa các phần tử khỏi tệp ArrayList. Vâng, gần như tất cả mọi thứ. Trong bài học tiếp theo, chúng ta sẽ tìm hiểu bên trong lớp này và xem điều gì xảy ra ở đó trong các cuộc gọi phương thức khác nhau :) Cho đến lúc đó!
Bình luận
  • Phổ biến
  • Mới
Bạn phải đăng nhập để đăng nhận xet
Trang này chưa có bất kỳ bình luận nào