Come rimuovere i duplicati da una lista in Java
Rimuovi i duplicati da una lista Java con HashSet, LinkedHashSet o stream distinct.
Una List in Java ammette elementi duplicati per design, quindi quando hai bisogno che ogni valore appaia una sola volta devi rimuovere le ripetizioni tu stesso. Questo capitolo mostra i modi idiomatici per farlo, prestando attenzione al fatto che l'ordine di inserimento originale venga preservato o meno.
Usare un LinkedHashSet (ordine preservato)
L'approccio più pulito è copiare la lista in un insieme, poiché un Set rifiuta i duplicati automaticamente. Usa LinkedHashSet invece di un semplice HashSet in modo che l'ordine del primo elemento incontrato venga mantenuto:
List<String> unique = new ArrayList<>(new LinkedHashSet<>(list));Avvolgere nuovamente l'insieme in un ArrayList restituisce una List, pronta per l'indicizzazione o per ulteriori elaborazioni. Il LinkedHashSet fa tutto il lavoro pesante: mentre viene riempito dalla lista originale, scarta silenziosamente qualsiasi elemento già visto, mentre la sua struttura collegata ricorda l'ordine in cui gli elementi sono arrivati per la prima volta.
Se non ti interessa l'ordine, un semplice HashSet è marginalmente più veloce e usa un po' meno memoria. Ma mescola l'ordine degli elementi, il che raramente è quello che vuoi quando visualizzi una lista, quindi LinkedHashSet è il default sicuro.
Usare la Stream API
Da Java 8 in poi, Stream.distinct() rimuove i duplicati in un'unica pipeline leggibile. Come LinkedHashSet, mantiene l'ordine di incontro degli elementi:
List<String> unique = list.stream()
.distinct()
.collect(Collectors.toList());distinct() confronta gli elementi con equals() e hashCode(), esattamente come fa un insieme, quindi i tuoi oggetti devono implementare quei metodi correttamente per i tipi personalizzati. Questa forma eccelle quando la deduplicazione è un passaggio in una pipeline più grande — puoi concatenare filter, map o sorted intorno ad essa senza introdurre una collection temporanea.
Confronto degli approcci
Entrambe le tecniche comuni si basano su equals/hashCode e preservano entrambe l'ordine di inserimento; la differenza è principalmente di stile e contesto.
| Approccio | Ordine mantenuto? | Ideale quando |
|---|---|---|
LinkedHashSet | Sì | Un one-liner rapido e senza dipendenze |
HashSet | No | L'ordine non importa e la velocità è critica |
stream().distinct() | Sì | La deduplicazione fa parte di una pipeline stream più grande |
Un punto chiave per tutti: costruiscono una nuova collection invece di mutare quella sorgente. Se devi deduplicare in-place, puoi invece svuotare la lista e re-aggiungere gli elementi univoci, oppure assegnare il risultato alla stessa variabile.
Esempio pratico
Cosa osservare dall'esecuzione:
- La lista originale mantiene tutti i 7 elementi, inclusi i ripetuti
javaesql, perché unaListammette i duplicati. - Il risultato del
LinkedHashSetha solo 4 elementi —[java, sql, api, rest]— e appaiono nell'ordine del primo incontro, non ordinati o mescolati. - Il risultato di
stream().distinct()è identico sia per dimensione che per ordine, confermando che le due tecniche sono intercambiabili in questo caso. deduped.equals(viaStream)stampatrue, poiché due liste sono uguali quando contengono gli stessi elementi nello stesso ordine.- La lista
tagsoriginale è invariata, quindi le operazioni di dedup hanno prodotto nuove liste senza mutare la sorgente.