1. Introduzione
Cominciamo con un esempio reale. Immagina di organizzare una festa e di compilare l’elenco degli ospiti. Spedisci gli inviti, e poi scopri che la stessa persona è finita nell’elenco due volte (o persino tre — beh, ama le feste!). Se usi una normale lista (List), tali duplicati possono comparire facilmente. Ma se avessi una collezione che di per sé non permettesse di aggiungere lo stesso ospite due volte — la vita sarebbe più semplice.
Qui entra in gioco la collezione Set — un insieme.
Set è una collezione che conserva solo elementi unici. Se provi ad aggiungere un elemento già esistente, semplicemente non verrà aggiunto (e nessuno se la prenderà).
Interfaccia Set: proprietà di base
In Java Set è un’interfaccia che definisce il comportamento di una collezione senza duplicati. Erede dell’interfaccia Collection, supporta quindi operazioni come aggiunta (add), rimozione (remove), verifica della presenza (contains) e iterazione.
Caratteristiche chiave:
- In Set non possono esserci due elementi uguali.
- Gli elementi possono essere memorizzati in ordine arbitrario — dipende dall’implementazione concreta.
- Nessun indice: non puoi accedere a un elemento per numero, come in una lista.
Sintassi di dichiarazione
Set<String> guests = new HashSet<>();
2. HashSet: veloce, semplice, senza ordine
HashSet è l’implementazione più popolare dell’interfaccia Set. Si basa su una tabella hash (come HashMap, solo senza la coppia «chiave-valore», ma con valori unici). Il suo principale vantaggio è la velocità delle operazioni di aggiunta, rimozione e ricerca.
Come funziona HashSet?
Immagina una scatola in cui metti le cose. Per capire rapidamente se c’è già qualcosa di simile, ogni cosa riceve il proprio «numero» — l’hash code. Quando aggiungi un elemento in HashSet, prima viene calcolato questo hash code. Se tale hash non è ancora presente, l’elemento viene inserito. Se l’hash esiste già, viene inoltre verificata l’uguaglianza tramite equals(). E solo se gli oggetti coincidono davvero, il nuovo elemento non viene aggiunto.
Quindi HashSet si occupa automaticamente dell’unicità: due oggetti identici non compariranno.
Un aspetto interessante: se lavori con le tue classi personalizzate e vuoi conservarle in un HashSet, devi sovrascrivere i metodi equals() e hashCode(). Senza questo la collezione può comportarsi in modo imprevedibile — oggetti apparentemente uguali potrebbero essere considerati diversi.
Metodi principali di HashSet
Set<String> guests = new HashSet<>();
guests.add("Ivan");
guests.add("Maria");
guests.add("Pietro");
guests.add("Ivan"); // Duplicato! Non verrà aggiunto.
System.out.println(guests); // [Ivan, Maria, Pietro] — l'ordine può essere qualsiasi
guests.remove("Pietro"); // Rimuoviamo l'elemento
System.out.println(guests.contains("Maria")); // true
System.out.println(guests.size())); // 2
Proviamolo nel codice
Supponiamo che nella nostra applicazione vogliamo memorizzare nomi unici di attività, in modo da non avere due attività con lo stesso nome:
import java.util.HashSet;
import java.util.Set;
public class UniqueTasksDemo {
public static void main(String[] args) {
Set<String> tasks = new HashSet<>();
tasks.add("Fare i compiti di Java");
tasks.add("Accarezzare il gatto");
tasks.add("Fare i compiti di Java"); // Duplicato!
System.out.println("Elenco delle attività:");
for (String task : tasks) {
System.out.println("- " + task);
}
// Nell'elenco ci saranno solo due attività, il duplicato non verrà aggiunto
}
}
3. TreeSet: l'ordine conta!
A volte non ci serve solo l’unicità, ma anche un insieme ordinato di elementi. Per esempio, ci piacerebbe vedere i nomi degli ospiti in ordine alfabetico e non casuale. Per questo c’è TreeSet.
TreeSet è un’implementazione dell’interfaccia Set che memorizza gli elementi in ordine ordinato (crescente). Si basa sulla struttura «albero rosso-nero».
Esempio di utilizzo di TreeSet
import java.util.Set;
import java.util.TreeSet;
public class SortedGuestsDemo {
public static void main(String[] args) {
Set<String> guests = new TreeSet<>();
guests.add("Vladimir");
guests.add("Aleksey");
guests.add("Ekaterina");
guests.add("Aleksey"); // Duplicato!
System.out.println("Ospiti (in ordine alfabetico):");
for (String guest : guests) {
System.out.println("- " + guest);
}
// Output:
// - Aleksey
// - Vladimir
// - Ekaterina
}
}
Nota: Se aggiungi un duplicato, non comparirà nell’insieme. Tutto come previsto!
Quando usare TreeSet?
- Quando serve un insieme ordinato di elementi unici.
- Quando è importante la ricerca veloce ma non è critica la velocità di inserimento (funziona un po’ più lentamente di HashSet).
- Se gli elementi sono classi personalizzate, devono essere «confrontabili» (implementare l’interfaccia Comparable) oppure devi fornire un tuo Comparator.
4. Dettagli utili
HashSet vs TreeSet: quale scegliere?
| Criterio | HashSet | TreeSet |
|---|---|---|
| Ordine di memorizzazione | Non garantito | Ordinato in ordine crescente |
| Velocità delle operazioni | Più veloce (O(1)) | Più lento (O(log n)) |
| Requisiti per il tipo | Qualsiasi (bastano equals()/hashCode()) | Comparable oppure Comparator |
| Scenari tipici | Quando serve accesso rapido a elementi unici | Quando è importante un output/attraversamento ordinato |
Caratteristiche di lavoro con Set
- Nessun indice. A differenza di List, Set non ha il metodo get(int index). Se ti serve l’accesso per indice — usa List.
- Niente duplicati. Se provi ad aggiungere un elemento già presente, non verrà aggiunto. Il metodo add restituirà false.
- Ordine non garantito (tranne TreeSet). In HashSet l’ordine degli elementi può variare a ogni esecuzione. Se ti serve l’ordine di inserimento, usa LinkedHashSet.
- Valori null.
- HashSet consente di memorizzare un solo elemento null.
- TreeSet non consente di aggiungere null senza uno Comparator dedicato, altrimenti verrà generata una NullPointerException.
5. Attività tipiche per Set
Rimozione dei duplicati da una lista
Supponiamo di avere una lista di studenti in cui alcuni sono registrati due volte. Dobbiamo lasciare solo i nomi unici:
import java.util.*;
public class RemoveDuplicatesDemo {
public static void main(String[] args) {
List<String> students = Arrays.asList("Anna", "Igor", "Anna", "Maria", "Igor", "Pavel");
Set<String> uniqueStudents = new HashSet<>(students);
System.out.println("Studenti unici: " + uniqueStudents);
// L'ordine non è garantito!
}
}
Se serve un risultato ordinato — usa TreeSet:
Set<String> sortedUniqueStudents = new TreeSet<>(students);
System.out.println("Studenti unici (in ordine alfabetico): " + sortedUniqueStudents);
Verifica dell'unicità (ad esempio, del login dell'utente)
Set<String> usedLogins = new HashSet<>();
usedLogins.add("student1");
usedLogins.add("java_lover");
String newLogin = "student1";
if (usedLogins.contains(newLogin)) {
System.out.println("Questo login è già occupato!");
} else {
System.out.println("Login disponibile!");
}
Iterazione degli elementi dell'insieme
L’iterazione si effettua tramite un ciclo for-each:
for (String name : uniqueStudents) {
System.out.println(name);
}
6. Errori tipici con Set
Errore n. 1: aspettarsi un certo ordine degli elementi in HashSet. Molti principianti si stupiscono del «modo strano» in cui vengono stampati gli elementi dell’insieme. È normale — HashSet non garantisce alcun ordine. Se serve l’ordine di inserimento — usa LinkedHashSet, se serve l’ordinamento — TreeSet.
Errore n. 2: tentare di accedere a un elemento per indice. A volte si prova a scrivere qualcosa come set.get(0). Non si può: Set non supporta l’indicizzazione. Se ti serve l’accesso per indice — usa List.
Errore n. 3: memorizzare oggetti mutabili. Se memorizzi oggetti che possono modificare i campi coinvolti in equals()/hashCode(), dopo tali modifiche l’elemento può «perdersi» per l’insieme. Rendi gli elementi immutabili o non modificare i campi identificativi.
Errore n. 4: aspettarsi che i duplicati vengano aggiunti. Aggiungere più volte lo stesso elemento non aumenterà la dimensione dell’insieme — i duplicati vengono ignorati, il metodo add restituirà false.
Errore n. 5: uso di tipi primitivi. Una dichiarazione come Set<int> non verrà compilata. Usa le classi wrapper: Set<Integer>, Set<Double> ecc.
GO TO FULL VERSION