1. Introducción
Empecemos con un ejemplo de la vida real. Imagina que organizas una fiesta y haces una lista de invitados. Envías las invitaciones y luego resulta que la misma persona aparece en la lista dos veces (o incluso tres — ¡le encantan las fiestas!). Si usas una lista normal (List), estos duplicados pueden colarse fácilmente. Pero si tuvieras una colección que no permitiera añadir dos veces a la misma persona, la vida sería más fácil.
Aquí entra en juego la colección Set — un conjunto.
Set es una colección que solo almacena elementos únicos. Si intentas añadir un elemento ya existente, simplemente no se añadirá (y nadie se ofenderá).
Interfaz Set: propiedades básicas
En Java, Set es una interfaz que define el comportamiento de una colección sin duplicados. Hereda de la interfaz Collection, lo que significa que admite operaciones como añadir (add), eliminar (remove), comprobar si existe un elemento (contains) y recorrerla.
Características clave:
- En un Set no puede haber dos elementos iguales.
- Los elementos pueden almacenarse en orden arbitrario — depende de la implementación concreta.
- No hay índices: no puedes acceder a un elemento por posición como en una lista.
Sintaxis de declaración
Set<String> guests = new HashSet<>();
2. HashSet: rápido, simple, sin orden
HashSet es la implementación más popular de la interfaz Set. Está basada en una tabla hash (como HashMap, pero sin el par «clave-valor», solo con valores únicos). Su principal ventaja es la rapidez en las operaciones de inserción, eliminación y búsqueda.
¿Cómo funciona HashSet?
Imagina una caja en la que metes cosas. Para saber rápidamente si ya hay dentro algo parecido, cada cosa recibe un «número» — un código hash. Cuando añades un elemento a HashSet, primero se calcula ese código hash. Si no se ha visto antes, el elemento se guarda sin problemas en la colección. Si ese hash ya existe, se comprueba además la igualdad mediante equals(). Solo si los objetos realmente coinciden, el nuevo elemento no se añade.
Es decir, HashSet se encarga automáticamente de la unicidad: no aparecerán dos objetos iguales.
Un detalle interesante: si trabajas con tus propias clases y quieres almacenarlas en un HashSet, tendrás que sobrescribir los métodos equals() y hashCode(). Sin esto, la colección puede comportarse de forma impredecible — objetos aparentemente iguales podrían considerarse distintos.
Métodos principales de HashSet
Set<String> guests = new HashSet<>();
guests.add("Ivan");
guests.add("Mariya");
guests.add("Pyotr");
guests.add("Ivan"); // ¡Duplicado! No se añadirá.
System.out.println(guests); // [Ivan, Mariya, Pyotr] — el orden puede ser cualquiera
guests.remove("Pyotr"); // Eliminamos un elemento
System.out.println(guests.contains("Mariya")); // true
System.out.println(guests.size()); // 2
Probémoslo en código
Supongamos que en nuestra aplicación queremos almacenar nombres de tareas únicos, para no tener dos tareas con el mismo título:
import java.util.HashSet;
import java.util.Set;
public class UniqueTasksDemo {
public static void main(String[] args) {
Set<String> tasks = new HashSet<>();
tasks.add("Hacer los deberes de Java");
tasks.add("Acariciar al gato");
tasks.add("Hacer los deberes de Java"); // ¡Duplicado!
System.out.println("Lista de tareas:");
for (String task : tasks) {
System.out.println("- " + task);
}
// En la lista habrá solo dos tareas; el duplicado no se añadirá
}
}
3. TreeSet: ¡el orden importa!
A veces necesitamos no solo unicidad, sino también un conjunto ordenado de elementos. Por ejemplo, queremos ver los nombres de los invitados en orden alfabético, y no en un orden aleatorio. Para ello existe TreeSet.
TreeSet es una implementación de la interfaz Set que almacena los elementos en orden (ascendente). Se basa en un «árbol rojo-negro».
Ejemplo de uso de 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"); // ¡Duplicado!
System.out.println("Invitados (por orden alfabético):");
for (String guest : guests) {
System.out.println("- " + guest);
}
// Salida:
// - Aleksey
// - Vladimir
// - Ekaterina
}
}
Ten en cuenta: Si añades un duplicado, no aparecerá en el conjunto. ¡Como debe ser!
¿Cuándo usar TreeSet?
- Cuando necesitas un conjunto de elementos únicos ordenado.
- Cuando importa la búsqueda rápida, pero no es crítica la velocidad de inserción (funciona un poco más lento que HashSet).
- Si los elementos son tus propias clases, deben ser «comparables» (implementar la interfaz Comparable) o debes proporcionar tu propio Comparator.
4. Matices útiles
HashSet vs TreeSet: ¿qué elegir?
| Criterio | HashSet | TreeSet |
|---|---|---|
| Orden de almacenamiento | No garantizado | Ordenado ascendentemente |
| Velocidad de las operaciones | Más rápido (O(1)) | Más lento (O(log n)) |
| Requisitos del tipo | Cualquiera (basta con equals()/hashCode()) | Comparable o Comparator |
| Escenarios típicos | Cuando se necesita acceso rápido a elementos únicos | Cuando es importante la salida/recorrido ordenado |
Particularidades al trabajar con Set
- Sin índices. A diferencia de List, Set no tiene el método get(int index). Si necesitas acceso por índice, usa List.
- Sin duplicados. Si intentas añadir un elemento que ya existe, no se añadirá. El método add devolverá false.
- No se garantiza el orden (salvo TreeSet). En HashSet el orden de los elementos puede variar en cada ejecución. Si necesitas preservar el orden de inserción, usa LinkedHashSet.
- Valores null.
- HashSet permite almacenar un único elemento null.
- TreeSet no permite añadir null sin un Comparator específico; de lo contrario, habrá una NullPointerException.
5. Tareas típicas para Set
Eliminar duplicados de una lista
Supongamos que tenemos una lista de estudiantes en la que algunos aparecen dos veces. Hay que dejar solo los nombres únicos:
import java.util.*;
public class RemoveDuplicatesDemo {
public static void main(String[] args) {
List<String> students = Arrays.asList("Anna", "Igor'", "Anna", "Mariya", "Igor'", "Pavel");
Set<String> uniqueStudents = new HashSet<>(students);
System.out.println("Estudiantes únicos: " + uniqueStudents);
// ¡El orden no está garantizado!
}
}
Si necesitas un resultado ordenado, usa TreeSet:
Set<String> sortedUniqueStudents = new TreeSet<>(students);
System.out.println("Estudiantes únicos (por orden alfabético): " + sortedUniqueStudents);
Comprobación de unicidad (por ejemplo, del nombre de usuario)
Set<String> usedLogins = new HashSet<>();
usedLogins.add("student1");
usedLogins.add("java_lover");
String newLogin = "student1";
if (usedLogins.contains(newLogin)) {
System.out.println("¡Ese nombre de usuario ya está ocupado!");
} else {
System.out.println("¡El nombre de usuario está disponible!");
}
Recorrido de los elementos de un conjunto
El recorrido se realiza con el bucle for-each:
for (String name : uniqueStudents) {
System.out.println(name);
}
6. Errores típicos al trabajar con Set
Error n.º 1: Esperar un orden concreto de los elementos en HashSet. A muchos principiantes les sorprende por qué los elementos del conjunto se muestran en un orden «extraño». Es normal — HashSet no garantiza un orden. Si necesitas el orden de inserción, usa LinkedHashSet; si necesitas ordenación, TreeSet.
Error n.º 2: Intentar acceder a un elemento por índice. A veces se intenta escribir algo como set.get(0). No se puede: Set no soporta indexación. Si necesitas acceso por índice, usa List.
Error n.º 3: Almacenar objetos mutables. Si almacenas objetos cuyos campos que participan en equals()/hashCode() pueden cambiar, después de modificar esos campos el elemento puede «perderse» para el conjunto. Haz los elementos inmutables o no cambies los campos que los identifican.
Error n.º 4: Esperar que se añadan los duplicados. Añadir el mismo elemento varias veces no aumentará el tamaño del conjunto — los duplicados se ignoran, el método add devolverá false.
Error n.º 5: Uso de tipos primitivos. Una declaración como Set<int> no compilará. Usa las clases envoltorio: Set<Integer>, Set<Double>, etc.
GO TO FULL VERSION