JavaRush /Blog Java /Random-PL /Usuwanie elementu z ArrayList w Javie

Usuwanie elementu z ArrayList w Javie

Opublikowano w grupie Random-PL
Cześć! Na ostatnim wykładzie zapoznaliśmy się z klasą ArrayList , a także dowiedzieliśmy się, jak wykonywać na niej najczęstsze operacje. Ponadto zidentyfikowaliśmy sporo różnic między ArrayList a zwykłą tablicą. Przyjrzyjmy się teraz usunięciu elementu z listy ArrayList. Powiedzieliśmy już, że usuwanie elementów zwykłej tablicy nie jest zbyt wygodne. Usuwanie elementu z ArrayList - 1Ponieważ nie możemy usunąć samej komórki, możemy jedynie „wyzerować” jej 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("Tomasz");
       cats[1] = new Cat("Hipopotam");
       cats[2] = new Cat(„Filip Markowicz”);

       cats[1] = null;

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


@Override
   public String toString() {
       return "Cat{" +
               "name='" + name + '\'' +
               '}';
   }
}
Wniosek:

[Cat{name='Томас'}, null, Cat{name='ФLubпп Маркович'}]
Ale po zresetowaniu w tablicy pozostaje „dziura”. Nie usuwamy komórki, a jedynie jej zawartość. Wyobraź sobie, co się stanie, jeśli będziemy mieli tablicę 50 kotów, z których 17 usunęliśmy w ten sposób. Będziemy mieli tablicę z 17 dołkami i będziemy się nimi opiekować! Zapamiętywanie na pamięć liczby pustych komórek, w których można zapisać nowe wartości, jest nierealne. Jeśli popełnisz raz błąd, nadpiszesz komórkę żądanym odniesieniem do obiektu. Jest oczywiście możliwość zrobienia tego nieco ostrożniej: po usunięciu należy przenieść elementy tablicy na początek, tak aby „dziura” znalazła się na końcu:
public static void main(String[] args) {

   Cat[] cats = new Cat[4];
   cats[0] = new Cat("Tomasz");
   cats[1] = new Cat("Hipopotam");
   cats[2] = new Cat(„Filip Markowicz”);
   cats[3] = new Cat("Puch");

   cats[1] = null;

   for (int i = 2; i < cats.length-1; i++) {
       //przenieś elementy na początek, tak aby pusta komórka znajdowała się na końcu
       cats[i-1] = cats[i];
       cats[i] = null;
   }

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

[Cat{name='Томас'}, Cat{name='ФLubпп Маркович'}, Cat{name='Пушок'}, null]
Teraz wydaje się, że wygląda to lepiej, ale trudno to nazwać stabilnym rozwiązaniem. Przynajmniej, ponieważ będziemy musieli pisać ten kod ręcznie za każdym razem, gdy usuniemy element z tablicy! Zła opcja. Możesz pójść w drugą stronę i stworzyć osobną metodę:
public void deleteCat(Cat[] cats, int indexToDelete) {
   //...usuń kota według indeksu i przesuń elementy
}
Ale to też jest mało przydatne: ta metoda może działać tylko z obiektami Cat, ale nie może działać z innymi. Oznacza to, że jeśli w programie jest jeszcze 100 klas, z którymi chcemy skorzystać z tablic, to w każdej z nich będziemy musieli napisać tę samą metodę z dokładnie tą samą logiką. To kompletna porażka -_- Ale w klasie ArrayList problem ten został pomyślnie rozwiązany! Implementuje specjalną metodę usuwania elementów - remove():
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Tomasz");
   Cat behemoth = new Cat("Hipopotam");
   Cat philipp = new Cat(„Filip Markowicz”);
   Cat pushok = new Cat("Puch");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);
   System.out.println(cats.toString());

   cats.remove(1);

   System.out.println(cats.toString());
}
Przekazaliśmy do metody indeks naszego obiektu, który został usunięty (tak jak w tablicy). Metoda remove()ma dwie cechy. Po pierwsze , nie pozostawia „dziur”. Implementuje już logikę przesuwania elementów przy usuwaniu elementu ze środka, którą wcześniej pisaliśmy ręcznie. Spójrz na wynik poprzedniego kodu w konsoli:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='ФLubпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='ФLubпп Маркович'}, Cat{name='Пушок'}]
Usunęliśmy jednego kota ze środka, a pozostałe przesunęliśmy tak, żeby nie było żadnych przerw. Po drugie , może usunąć obiekt nie tylko według indeksu (jak zwykła tablica), ale także poprzez odniesienie do obiektu :
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Tomasz");
   Cat behemoth = new Cat("Hipopotam");
   Cat philipp = new Cat(„Filip Markowicz”);
   Cat pushok = new Cat("Puch");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);
   System.out.println(cats.toString());

   cats.remove(philipp);

   System.out.println(cats.toString());
}
Wniosek:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='ФLubпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
Może to być bardzo wygodne, jeśli nie chcesz zawsze mieć w głowie indeksu żądanego obiektu. Wygląda na to, że uporaliśmy się ze zwykłym usuwaniem. Teraz wyobraźmy sobie taką sytuację: chcemy iterować po naszej liście elementów i usunąć kota o określonym imieniu. W tym celu używamy specjalnego operatora pętli for- for each. Więcej na ten temat dowiesz się z tego wykładu .
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Tomasz");
   Cat behemoth = new Cat("Hipopotam");
   Cat philipp = new Cat(„Filip Markowicz”);
   Cat pushok = new Cat("Puch");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);

   for (Cat cat: cats) {

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

   System.out.println(cats);
}
Kod wydaje się wyglądać całkiem logicznie. Jednak wynik może Cię zaskoczyć:

Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
  at java.util.ArrayList$Itr.next(ArrayList.java:831)
  at Cat.main(Cat.java:25)
Jakiś błąd i nie jest jasne, dlaczego nagle się pojawił. W tym procesie istnieje wiele niuansów, którymi należy się zająć. Ogólna zasada, o której musisz pamiętać: Nie możesz iterować po kolekcji i jednocześnie zmieniać jej elementów. Tak, tak, dokładnie zmiana, a nie tylko usunięcie. Jeśli spróbujesz w naszym kodzie zastąpić usuwanie kotów wstawieniem nowych, efekt będzie taki sam:
for (Cat cat: cats) {

   cats.add(new Cat(„Salem Saberhegen”));
}

System.out.println(cats);

Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
  at java.util.ArrayList$Itr.next(ArrayList.java:831)
  at Cat.main(Cat.java:25)
Zmieniliśmy jedną operację na drugą, ale wynik się nie zmienił: ten sam błąd ConcurrentModificationException. Dzieje się tak właśnie wtedy, gdy podczas iteracji po niej próbujemy złamać regułę i zmienić listę. W Javie, aby usunąć elementy podczas iteracji, należy użyć specjalnego obiektu - iteratora (klasy Iterator). Zadaniem klasy Iteratorjest bezpieczne przejście przez listę elementów. Jest to dość proste, ponieważ ma tylko 3 metody:
  • hasNext()- zwraca truealbo falsew zależności od tego, czy na liście znajduje się kolejny element, czy też dotarliśmy już do ostatniego.
  • next()- zwraca kolejny element listy
  • remove()- usuwa element z listy
Jak widać iterator jest dosłownie „szyty” na nasze potrzeby i nie ma w nim nic skomplikowanego. Przykładowo chcemy sprawdzić czy nasza lista zawiera następujący element i jeśli tak to wypisać go na konsolę:
Iterator<Cat> catIterator = cats.iterator();//utwórz iterator
while(catIterator.hasNext()) {// dopóki na liście znajdują się elementy

   Cat nextCat = catIterator.next();// pobierz następny element
   System.out.println(nextCat);// wypisz to na konsoli
}
Wniosek:

Cat{name='Томас'}
Cat{name='Бегемот'}
Cat{name='ФLubпп Маркович'}
Cat{name='Пушок'}
Jak widać, klasa ArrayListimplementuje już specjalną metodę tworzenia iteratora - iterator(). Należy również pamiętać, że tworząc iterator, określamy klasę obiektów, z którymi będzie on współpracował ( <Cat>). Ostatecznie możemy łatwo rozwiązać nasz pierwotny problem za pomocą iteratora. Na przykład usuńmy kota o imieniu „Philip Markovich”:
Iterator<Cat> catIterator = cats.iterator();//utwórz iterator
while(catIterator.hasNext()) {// dopóki na liście znajdują się elementy

   Cat nextCat = catIterator.next();// pobierz następny element
   if (nextCat.name.equals(„Filip Markowicz”)) {
       catIterator.remove();//usuń kota o żądanej nazwie
   }
}

System.out.println(cats);
Wniosek:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
Być może zauważyłeś, że w metodzie iteratora nie określiliśmy ani indeksu elementu, ani nazwy zmiennej referencyjnej remove()! Iterator jest mądrzejszy niż mogłoby się wydawać: metoda remove()usuwa ostatni element zwrócony przez iterator. Jak widać, zadziałało dokładnie tak, jak trzeba :) To w zasadzie wszystko, co musisz wiedzieć o usuwaniu elementów z ArrayList. Dokładniej – prawie wszystko. Na kolejnym wykładzie zajrzymy do „wnętrza” tej klasy i zobaczymy, co się tam dzieje podczas operacji :) Do zobaczenia!
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION