Usuwanie elementów z Kolekcji Java

Przegląd

W tym krótkim poradniku, będziemy mówić o czterech różnych sposobach usuwania elementów z Kolekcji Java, które pasują do pewnych predykatów.

Definiowanie naszej kolekcji

Najpierw zilustrujemy dwa podejścia, które mutują oryginalną strukturę danych. Następnie porozmawiamy o dwóch innych opcjach, które zamiast usuwać elementy, utworzą kopię oryginalnej Kolekcji bez nich.

W naszych przykładach użyjmy następującej kolekcji, aby zademonstrować, jak możemy osiągnąć ten sam rezultat, używając różnych metod:

Collection<String> names = new ArrayList<>();names.add("John");names.add("Ana");names.add("Mary");names.add("Anthony");names.add("Mark");

Usuwanie elementów za pomocą Iteratora

Iterator w Javie umożliwia nam zarówno chodzenie, jak i usuwanie każdego pojedynczego elementu w Kolekcji.

Aby to zrobić, musimy najpierw pobrać iterator nad jej elementami za pomocą metody iterator. Następnie możemy odwiedzić każdy element za pomocą next i usunąć je za pomocą remove:

Iterator<String> i = names.iterator();while(i.hasNext()) { String e = i.next(); if (e.startsWith("A")) { i.remove(); }}

Pomimo swojej prostoty, istnieją pewne zastrzeżenia, które powinniśmy rozważyć:

  • W zależności od kolekcji możemy natknąć się na wyjątki ConcurrentModificationException
  • Musimy iterować nad elementami, zanim będziemy mogli je usunąć
  • W zależności od kolekcji, remove może zachowywać się inaczej niż oczekiwano. Np: ArrayList.Iterator usuwa element z kolekcji i przesuwa kolejne dane w lewo, podczas gdy, LinkedList.Iterator po prostu dostosowuje wskaźnik do następnego elementu. Jako taki, LinkedList.Iterator wykonuje znacznie lepiej niż ArrayList.Iterator podczas usuwania elementów

Java 8 i Collection.removeIf()

Java 8 wprowadziła nową metodę do interfejsu Collection, która zapewnia bardziej zwięzły sposób usuwania elementów przy użyciu Predicate:

names.removeIf(e -> e.startsWith("A"));

Warto zauważyć, że w przeciwieństwie do podejścia Iteratora, removeIf wykonuje się podobnie dobrze zarówno w LinkedList, jak i ArrayList.

W Javie 8, ArrayList zastępuje domyślną implementację – która opiera się na Iterator – i implementuje inną strategię: najpierw iteruje po elementach i zaznacza te, które pasują do naszego predykatu; następnie iteruje po raz drugi, aby usunąć (i przesunąć) elementy, które zostały zaznaczone w pierwszej iteracji.

Java 8 i wprowadzenie Strumienia

Jedną z nowych głównych funkcji w Javie 8 było dodanie Strumienia (i Kolektorów). Istnieje wiele sposobów na utworzenie Strumienia z jakiegoś źródła. Jednak większość operacji, które wpływają na instancję Strumienia, nie zmutuje jego źródła, a raczej API skupia się na tworzeniu kopii źródła i wykonywaniu na nich dowolnych operacji, których możemy potrzebować.

Przyjrzyjrzyjmy się, jak możemy użyć Strumienia i Kolektorów do znalezienia/filtrowania elementów, które pasują i nie pasują do naszego predykatu.

5.1. Usuwanie elementów za pomocą Stream

Usuwanie, a raczej filtrowanie elementów za pomocą Stream jest dość proste, musimy tylko utworzyć instancję Stream za pomocą naszej Kolekcji, wywołać filtr za pomocą naszego Predykatu, a następnie zebrać wynik za pomocą Kolektorów:

Collection<String> filteredCollection = names .stream() .filter(e -> !e.startsWith("A")) .collect(Collectors.toList());

Streaming jest mniej inwazyjny niż poprzednie podejścia, promuje izolację i umożliwia tworzenie wielu kopii z tego samego źródła. Należy jednak pamiętać, że zwiększa on również ilość pamięci wykorzystywanej przez naszą aplikację.

5.2. Collectors.partitioningBy

Połączenie zarówno Stream.filter, jak i Collectors jest całkiem poręczne, choć możemy natknąć się na scenariusze, w których potrzebujemy zarówno pasujących, jak i niepasujących elementów. W takich przypadkach możemy skorzystać z Collectors.partitioningBy:

Map<Boolean, List<String>> classifiedElements = names .stream() .collect(Collectors.partitioningBy((String e) -> !e.startsWith("A")));String matching = String.join(",", classifiedElements.get(true));String nonMatching = String.join(",", classifiedElements.get(false));

Metoda ta zwraca Mapę, która zawiera tylko dwa klucze, true i false, z których każdy wskazuje na listę zawierającą odpowiednio pasujące i niepasujące elementy.

Podsumowanie

W tym artykule przyjrzeliśmy się niektórym metodom usuwania elementów z Kolekcji i kilku ich zastrzeżeniom.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.