Menu

HashSet in Java: valori unici, appartenenza e operazioni sugli insiemi

Come usare l'HashSet di Java per collezioni di valori unici: add, contains, remove, togliere i duplicati da una lista e combinare insiemi con unione, intersezione e differenza.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Un set memorizza valori unici

Un HashSet (di java.util) è una collezione che contiene ogni valore al massimo una volta. Non ci sono chiavi e valori abbinati come in una HashMap: solo un contenitore di elementi distinti. Il suo compito è rispondere in fretta a una sola domanda: "questa cosa è qui dentro?"

C'è un solo parametro di tipo, <ElementType>. Come per ArrayList e HashMap, di solito dichiari la variabile con l'interfaccia Set e costruisci un HashSet.

add ti dice se il valore era nuovo

add non si limita a memorizzare il valore: restituisce un boolean che ti dice se l'insieme è effettivamente cambiato. Aggiungere un valore già presente restituisce false e lascia l'insieme invariato.

Quel valore di ritorno è davvero utile: if (!seen.add(x)) { /* x is a repeat */ } ti permette di rilevare i duplicati in una sola riga mentre procedi.

Rimuovere i duplicati da una lista

Dato che un set rifiuta le ripetizioni, il modo più veloce per eliminare i duplicati da una collezione è riversarla in un set. Il costruttore di HashSet accetta qualsiasi altra collezione:

È il motivo più comune per cui i principianti usano un set. Sappi solo che nel passaggio perdi l'ordine originale: usa LinkedHashSet se l'ordine conta (ne parliamo più sotto).

contains, remove e size

Le operazioni di tutti i giorni rispecchiano quelle delle altre collezioni:

Il grande vantaggio rispetto a un ArrayList è contains. Una lista deve scorrere ogni elemento per rispondere (O(n)); un HashSet arriva quasi direttamente alla risposta (circa O(1)). Quando ti ritrovi a chiamare list.contains(...) dentro un ciclo, di solito è il segnale per passare a un set.

Operazioni sugli insiemi: unione, intersezione, differenza

I set danno il meglio quando li combini. I metodi si leggono in modo naturale una volta capito quale fa cosa:

La trappola principale: addAll, retainAll e removeAll modificano l'insieme su cui vengono chiamati. Ecco perché ogni esempio copia prima a in un nuovo HashSet: altrimenti distruggeresti l'originale. Crea un nuovo set per ogni risultato.

HashSet non mantiene l'ordine

Come HashMap, un HashSet non garantisce alcun ordine di iterazione, e l'ordine può cambiare da un'esecuzione all'altra. Se ti serve prevedibilità:

  • LinkedHashSet mantiene l'ordine di inserimento, cioè l'ordine in cui hai aggiunto gli elementi.
  • TreeSet tiene gli elementi ordinati secondo l'ordine naturale (o secondo un Comparator che fornisci tu).

Tutte e tre implementano l'interfaccia Set, quindi passare dall'una all'altra è una modifica di una riga al costruttore.

Gli elementi devono essere hashabili

Dietro le quinte un HashSet si appoggia a una HashMap, quindi vale la stessa regola: trova gli elementi calcolandone l'hash, il che significa che hashCode() ed equals() di un elemento devono essere coerenti. I tipi predefiniti come String e Integer lo fanno già correttamente, ed è per questo che le stringhe "java" duplicate qui sopra vengono unite correttamente. Se memorizzi istanze di una tua classe, fai l'override sia di equals sia di hashCode: altrimenti due oggetti "uguali" nel significato verranno trattati come distinti, e contains e la rimozione dei duplicati falliranno in silenzio.

Prossimo passo: scorrere le collezioni

Ora conosci le tre collezioni fondamentali: ArrayList, HashMap e HashSet. Ognuna si scorre in modo un po' diverso, e ci sono trappole sottili (come modificare una collezione mentre la stai scorrendo). Nella prossima pagina metteremo tutto insieme e vedremo come scorrere le collezioni in modo pulito con il ciclo for-each, gli iteratori e forEach.

Domande frequenti

Come si crea un HashSet in Java?

Dichiaralo con un parametro di tipo, il tipo degli elementi, e chiama il costruttore: Set<String> tags = new HashSet<>();. Aggiungi valori con tags.add("java"); e verifica l'appartenenza con tags.contains("java");. Importa java.util.HashSet e java.util.Set.

Qual è la differenza tra un HashSet e un ArrayList in Java?

Un ArrayList conserva ogni elemento che aggiungi (duplicati compresi) nell'ordine di inserimento ed è indicizzato per posizione. Un HashSet memorizza solo valori unici, non garantisce alcun ordine, non ha indici e il suo controllo contains richiede un tempo circa costante invece di scorrere tutta la lista. Usa un HashSet quando ti interessano l'unicità o la verifica rapida di appartenenza, non la posizione.

Come si rimuovono i duplicati da una lista in Java?

Passa la lista al costruttore di un HashSet: Set<String> unique = new HashSet<>(list);. L'insieme scarta automaticamente i valori ripetuti. Se ti serve di nuovo una lista (e non ti importa di perdere l'ordine), avvolgilo di nuovo: new ArrayList<>(unique). Usa invece un LinkedHashSet se vuoi mantenere l'ordine originale.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA