CodeGym /Blog Java /Random-PL /Usuwanie elementu z ArrayList
Autor
Volodymyr Portianko
Java Engineer at Playtika

Usuwanie elementu z ArrayList

Opublikowano w grupie Random-PL
Cześć! Na ostatniej lekcji zapoznaliśmy się z ArrayListklasą i nauczyliśmy się wykonywać na niej najczęstsze operacje. Ponadto wskazaliśmy kilka różnic między tablicą ArrayLista zwykłą tablicą. Ale pominęliśmy jeden temat, a mianowicie, jak usunąć elementy z pliku ArrayList. Omówimy to teraz. Usuwanie elementu z tablicy ArrayList — 1Wspomnieliśmy już, że usuwanie elementów ze zwykłej tablicy nie jest zbyt wygodne. Ponieważ nie możemy usunąć samego elementu, możemy jedynie „wyzerować” (ustawić na zero) jego wartość:

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 + '\'' +
               '}';
   }
}
Dane wyjściowe: [Cat{name='Thomas'}, null, Cat{name='Lionel Messi'}] Ale ustawienie elementu tablicy na null pozostawia „dziurę”. Nie usunęliśmy pozycji w tablicy, tylko jej zawartość. Wyobraź sobie, co by się stało, gdybyśmy mieli tablicę 50 kotów i usunęli 17 z nich w ten sposób. Będziemy mieć tablicę z 17 otworami. Po prostu spróbuj je śledzić! Oczekiwanie, że zapamiętasz liczbę pustych komórek, w których możesz wpisać nowe wartości, jest nierealistyczne. Jeśli popełnisz jeden błąd, nadpiszesz żądane odniesienie do obiektu. Jest oczywiście sposób, aby zrobić to nieco ostrożniej: po usunięciu elementu przesuń elementy na przód tablicy, aby umieścić „dziurę” na końcu:

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));
}
Wynik: [Cat{name='Thomas'}, Cat{name='Fluffy'}, Cat{name='Fluffy'}, null] To wydaje się lepsze, ale trudno to nazwać niezawodnym rozwiązaniem. Choćby z tego powodu, że musimy pisać ten kod za każdym razem, gdy usuwamy element z tablicy! To zła opcja. Moglibyśmy pójść inną drogą i stworzyć osobną metodę:

public void deleteCat(Cat[] cats, int indexToDelete) {
   //...delete the cat corresponding to the index and move the elements
}
Ale jest to również mało przydatne: ta metoda może działać tylko z Catobiektami, ale nie z innymi typami. Innymi słowy, jeśli program ma kolejnych 100 klas, których chcemy użyć z tablicami, będziemy musieli napisać tę samą metodę z dokładnie taką samą logiką w każdej z nich. To totalna katastrofa -_- Ale ArrayListklasa rozwiązuje ten problem! Implementuje specjalną metodę usuwania elementów: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());
}
Przekazujemy indeks naszego obiektu do metody, która go usuwa (tak jak w tablicy). Metoda remove()ma dwie cechy szczególne. Po pierwsze nie pozostawia „dziur”. Implementuje już logikę potrzebną do przesuwania elementów, gdy element jest usuwany ze środka, co wcześniej sami napisaliśmy. Spójrz na dane wyjściowe z poprzedniego kodu:

[Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]

[Cat{name='Thomas'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]
Usunęliśmy jednego kota ze środka, a resztę przenieśliśmy tak, aby nie było pustych miejsc. Po drugie , może usuwać obiekty nie tylko według indeksu (jak normalna tablica), ale także przez referencje :

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());
}
Wynik: [Kot{imię='Thomas'}, Kot{imię='Behemot'}, Kot{imię='Lionel Messi'}, Kot{imię='Fluffy'}] [Kot{imię='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] Może to być bardzo wygodne, jeśli nie chcesz zawsze śledzić indeksu żądanego obiektu. Wygląda na to, że odkryliśmy zwykłe usuwanie. Teraz wyobraźmy sobie taką sytuację: chcemy iterować po naszej liście i usunąć kota o określonej nazwie . Aby to zrobić, użyjemy szybkiej forpętli (zwanej także pętlą for-each), którą poznaliśmy na lekcjach Rishiego:

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);
}
Kod wygląda całkowicie logicznie. Ale wynik może być dużym zaskoczeniem: wyjątek w wątku „main” java.util.ConcurrentModificationException w java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) w java.util.ArrayList$Itr.next(ArrayList. java:831) w Cat.main(Cat.java:25) Wystąpił jakiś błąd i nie jest jasne, dlaczego tak się stało. Ten proces obejmuje wiele niuansów, które należy uwzględnić. Oto ogólna zasada, o której musisz pamiętać: nie możesz jednocześnie iterować kolekcji i zmieniać jej elementów. I mamy na myśli wszelkiego rodzaju zmiany, nie tylko usunięcie. Jeśli zastąpisz usunięcie kota próbą wstawienia nowych kotów, rezultat będzie taki sam:

for (Cat cat: cats) {

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

System.out.println(cats);
Wyjątek w wątku „main” java.util.ConcurrentModificationException w java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) w java.util.ArrayList$Itr.next(ArrayList.java:831) w Cat.main( Cat.java:25) Zmieniliśmy jedną operację na inną, ale wynik się nie zmienił: otrzymujemy ten sam wyjątek ConcurrentModificationException . Dzieje się tak właśnie wtedy, gdy próbujemy złamać powyższą regułę, zmieniając listę podczas iteracji po niej. W Javie potrzebujemy specjalnego obiektu zwanego iteratorem ( klasąIterator), aby usuwać elementy podczas iteracji kolekcji. KlasaIteratorjest odpowiedzialna za bezpieczne iterowanie po liście elementów. Jest to dość proste, ponieważ ma tylko 3 metody:
  • hasNext()- zwraca true lub false w zależności od tego, czy na liście znajduje się kolejna pozycja, czy też dotarliśmy już do ostatniej.
  • next()- zwraca następny element na liście
  • remove()- usuwa element z listy
Jak widać, iterator jest skrojony na miarę naszych potrzeb, a jednocześnie nie ma w nim nic skomplikowanego. Załóżmy, że chcemy sprawdzić, czy na naszej liście jest kolejny element i wyświetlić go, jeśli jest:

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
}
Wyjście: Kot{name='Thomas'} Kot{name='Behemoth'} Kot{name='Lionel Messi'} Kot{name='Fluffy'} Jak widać, program zaimplementował już specjalną ArrayListmetodę tworzenia iterator: iterator(). Dodatkowo zauważ, że kiedy tworzymy iterator, określamy klasę obiektów, z którymi będzie on współpracował ( <Cat>). Najważniejsze jest to, że iterator z łatwością radzi sobie z naszym pierwotnym zadaniem. Na przykład usuń kota o imieniu „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);
Wyjście: [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] Być może zauważyłeś, że nie określiliśmy ani indeksu, ani nazwy w remove()metodzie iteratora ! Iterator jest sprytniejszy niż mogłoby się wydawać: remove()usuwa ostatni element zwrócony przez iterator. Jak widać, zrobił dokładnie to, czego chcieliśmy :) W zasadzie to wszystko, co musisz wiedzieć o usuwaniu elementów z pliku ArrayList. No prawie wszystko. W następnej lekcji zajrzymy do tej klasy i zobaczymy, co się tam dzieje podczas wywołań różnych metod :) Do tego czasu!
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION