Vizualizare generală
În acest tutorial rapid, vom vorbi despre patru moduri diferite de a elimina elemente din colecțiile Java care corespund anumitor predicte.
În mod firesc, vom analiza și unele dintre avertismente.
Definirea colecției noastre
În primul rând, vom ilustra două abordări care modifică structura de date originală. Apoi vom vorbi despre alte două opțiuni care, în loc să elimine elementele, vor crea o copie a colecției originale fără ele.
Să folosim următoarea colecție pe parcursul exemplelor noastre pentru a demonstra cum putem obține același rezultat folosind metode diferite:
Collection<String> names = new ArrayList<>();names.add("John");names.add("Ana");names.add("Mary");names.add("Anthony");names.add("Mark");
Remiterea elementelor cu Iterator
Iteratorul Java ne permite atât să parcurgem cât și să eliminăm fiecare element individual dintr-o colecție.
Pentru a face acest lucru, trebuie mai întâi să recuperăm un iterator peste elementele sale folosind metoda iterator. Ulterior, putem vizita fiecare element cu ajutorul metodei next și le putem elimina folosind remove:
Iterator<String> i = names.iterator();while(i.hasNext()) { String e = i.next(); if (e.startsWith("A")) { i.remove(); }}
În ciuda simplității sale, există câteva avertismente pe care ar trebui să le luăm în considerare:
- În funcție de colecție, este posibil să ne confruntăm cu excepții de tip ConcurrentModificationException
- Trebuie să iterăm peste elemente înainte de a le putea elimina
- În funcție de colecție, remove se poate comporta altfel decât ne așteptăm. De ex: ArrayList.Iterator elimină elementul din colecție și deplasează datele ulterioare la stânga, în timp ce, LinkedList.Iterator ajustează pur și simplu pointerul la următorul element. Ca atare, LinkedList.Iterator se comportă mult mai bine decât ArrayList.Iterator atunci când elimină elemente
Java 8 și Collection.removeIf()
Java 8 a introdus o nouă metodă în interfața Collection care oferă o modalitate mai concisă de a elimina elemente folosind Predicate:
names.removeIf(e -> e.startsWith("A"));
Este important de remarcat că, spre deosebire de abordarea Iterator, removeIf se comportă la fel de bine atât în LinkedList, cât și în ArrayList.
În Java 8, ArrayList suprascrie implementarea implicită – care se bazează pe Iterator – și implementează o strategie diferită: mai întâi, itera peste elementele și le marchează pe cele care se potrivesc cu Predicate-ul nostru; după aceea, itera a doua oară pentru a elimina (și deplasa) elementele care au fost marcate în prima iterație.
Java 8 și introducerea Stream-ului
Una dintre noile caracteristici majore din Java 8 a fost adăugarea Stream-ului (și a colectoarelor). Există mai multe moduri de a crea un Stream dintr-o sursă. Cu toate acestea, majoritatea operațiilor care afectează instanța Stream nu vor modifica sursa sa, mai degrabă, API-ul se concentrează pe crearea de copii ale unei surse și pe efectuarea oricărei operații de care am putea avea nevoie în ele.
Să vedem cum putem folosi Stream și Collectors pentru a găsi/filtra elementele care se potrivesc, și nu se potrivesc, cu predicatul nostru.
5.1. Eliminarea elementelor cu Stream
Îndepărtarea sau, mai degrabă, filtrarea elementelor folosind Stream este destul de simplă, trebuie doar să creăm o instanță de Stream folosind Colecția noastră, să invocăm filtrul cu Predicatul nostru și apoi să colectăm rezultatul cu ajutorul Colecționarilor:
Collection<String> filteredCollection = names .stream() .filter(e -> !e.startsWith("A")) .collect(Collectors.toList());
Streaming-ul este mai puțin invaziv decât abordările anterioare, promovează izolarea și permite crearea de copii multiple din aceeași sursă. Cu toate acestea, trebuie să ținem cont de faptul că, de asemenea, crește memoria utilizată de aplicația noastră.
5.2. Collectors.partitioningBy
Combinarea atât a Stream.filter cât și a Collectors este destul de utilă, deși este posibil să ne confruntăm cu scenarii în care avem nevoie atât de elemente care se potrivesc, cât și de elemente care nu se potrivesc. În astfel de cazuri, putem profita de 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));
Această metodă returnează un Map care conține doar două chei, true și false, fiecare dintre ele arătând către o listă care conține elementele care se potrivesc și, respectiv, cele care nu se potrivesc.
Concluzie
În acest articol, am analizat câteva metode de eliminare a elementelor din Colecții și câteva dintre avertismentele lor.
.