Rimozione di elementi dalle raccolte Java

1. Panoramica

In questo breve tutorial, parleremo di quattro diversi modi per rimuovere elementi dalle raccolte Java che corrispondono a determinati predicati.

Naturalmente esamineremo anche alcuni degli avvertimenti.

2. Definizione della nostra collezione

Innanzitutto, illustreremo due approcci che mutano la struttura dei dati originale. Quindi parleremo di altre due opzioni che invece di rimuovere gli elementi, creeranno una copia della Collezione originale senza di loro.

Usiamo la seguente raccolta in tutti i nostri esempi per dimostrare come possiamo ottenere lo stesso risultato utilizzando metodi diversi:

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

3. Rimozione di elementi con Iterator

L' iteratore di Java ci consente sia di camminare che di rimuovere ogni singolo elemento all'interno di una raccolta .

Per fare ciò, dobbiamo prima recuperare un iteratore sui suoi elementi usando il metodo iteratore . Successivamente, possiamo visitare ogni elemento con l'aiuto di next e rimuoverli usando remove :

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

Nonostante la sua semplicità, ci sono alcuni avvertimenti che dovremmo considerare:

  • A seconda della raccolta, potremmo incorrere in eccezioni ConcurrentModificationException
  • Dobbiamo iterare sugli elementi prima di poterli rimuovere
  • A seconda della raccolta, la rimozione potrebbe comportarsi in modo diverso dal previsto. Ad esempio: ArrayList.Iterator rimuove l'elemento dalla raccolta e sposta i dati successivi a sinistra, mentre LinkedList.Iterator regola semplicemente il puntatore all'elemento successivo. Pertanto, LinkedList.Iterator funziona molto meglio di ArrayList.Iterator durante la rimozione di elementi

4. Java 8 e Collection.removeIf ()

Java 8 ha introdotto un nuovo metodo per la Collezione interfaccia che fornisce un modo più conciso per rimuovere elementi utilizzando Predicate :

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

È importante notare che, contrariamente all'approccio Iterator , removeIf funziona in modo simile sia in LinkedList che in ArrayList .

In Java 8, ArrayList sovrascrive l'implementazione predefinita - che si basa su Iterator - e implementa una strategia diversa: innanzitutto, itera sugli elementi e contrassegna quelli che corrispondono al nostro Predicate; in seguito, itera una seconda volta per rimuovere (e spostare) gli elementi che erano stati contrassegnati nella prima iterazione.

5. Java 8 e l'introduzione di Stream

Una delle nuove caratteristiche principali di Java 8 è stata l'aggiunta di Stream (e Collectors ). Esistono molti modi per creare un flusso da una sorgente. Tuttavia, la maggior parte delle operazioni che influiscono sull'istanza Stream non ne modificheranno l'origine, ma piuttosto l'API si concentra sulla creazione di copie di un'origine e sull'esecuzione di qualsiasi operazione di cui abbiamo bisogno.

Diamo un'occhiata a come possiamo usare Stream e Collectors per trovare / filtrare gli elementi che corrispondono e non corrispondono al nostro Predicate .

5.1. Rimozione di elementi con flusso

Rimuovere, o meglio filtrare elementi utilizzando Stream, è abbastanza semplice , dobbiamo solo creare un'istanza di Stream utilizzando la nostra Collection , invocare il filtro con il nostro Predicate e quindi raccogliere il risultato con l'aiuto dei Collectors:

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

Lo streaming è meno invasivo rispetto agli approcci precedenti, favorisce l'isolamento e consente la creazione di più copie dalla stessa fonte. Tuttavia, dobbiamo tenere presente che aumenta anche la memoria utilizzata dalla nostra applicazione.

5.2. Collectors.partitioningBy

La combinazione di Stream.filter e Collectors è piuttosto utile, sebbene potremmo imbatterci in scenari in cui abbiamo bisogno di elementi corrispondenti e non corrispondenti. In questi casi possiamo sfruttare Collectors.partitioningBy :

Map
    
      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));
    

Questo metodo restituisce una mappa che contiene solo due chiavi, true e false , ciascuna che punta a un elenco che contiene rispettivamente gli elementi corrispondenti e non corrispondenti.

6. Conclusione

In questo articolo, abbiamo esaminato alcuni metodi per rimuovere elementi dalle raccolte e alcuni dei loro avvertimenti.

Puoi trovare il codice sorgente completo e tutti i frammenti di codice per questo articolo su GitHub.