Un Set es una colección que no admite elementos duplicados según su criterio de igualdad.
Imagina que estás organizando una boda. Tienes una lista de invitados en un Excel (List). Por error, has copiado y pegado varias veces y ahora “Tía Paquita” aparece cuatro veces.
Si envías las invitaciones usando la List, Tía Paquita recibirá 4 cartas. Mal asunto.
Lo que necesitas es una estructura que diga: “Si Tía Paquita ya está en la lista, ignora los duplicados”.
Necesitas un Conjunto (Set).
¿Qué es un set?
Un Set es una colección que tiene dos reglas sagradas:
- NO admite duplicados: Si intentas añadir algo que ya existe (
add()), la operación devuelvefalsey no hace nada. - NO tiene acceso por índice: No existe el método
get(0). No puedes decir “dame el primero”, porque en una bolsa de canicas no hay “primero” ni “segundo”. Solo puedes preguntar “¿Está la canica roja?” (contains).
Las tres implementaciones (la tríada)
Java nos ofrece tres formas de usar Sets. Todas garantizan unicidad, pero se comportan muy distinto respecto al orden y la velocidad.
Es la implementación por defecto y la más usada.
- Orden: Ninguno. Los elementos quedan “desordenados” (técnicamente, ordenados por su hash interno, que para ti parece aleatorio).
- Velocidad: Extremadamente rápido (O(1)) para insertar y buscar.
- Uso: Cuando solo quieres eliminar duplicados y te da igual en qué orden salgan los datos.
Es un HashSet con memoria.
- Orden: Mantiene el orden de inserción. Si metiste “A”, luego “B” y luego “C”, al recorrerlo saldrán “A, B, C”.
- Velocidad: Ligeramente más lento que el HashSet (porque tiene que mantener punteros del orden).
- Uso: Cuando quieres evitar duplicados pero necesitas que la interfaz gráfica muestre los datos en el mismo orden que los introdujo el usuario.
Los elementos se guardan en un árbol Rojo-Negro.
- Orden: Orden Natural (Alfabético para Strings, numérico para Integers).
- Velocidad: El más lento de los tres (O(logn)). Insertar cuesta más porque Java tiene que buscar el hueco correcto para mantener el orden alfabético en tiempo real.
- Uso: Cuando necesitas que la lista esté siempre ordenada alfabéticamente automáticamente.
Ejemplo comparativo
Vamos a meter los mismos datos en los tres sets y ver qué sale.
Datos a insertar: "Zara", "Ana", "Zara" (repetido), "Pedro".
import java.util.*;
public class BatallaSets {
public static void main(String[] args) {
// 1. HashSet: Caos total (pero rápido)
Set<String> hashSet = new HashSet<>();
hashSet.add("Zara");
hashSet.add("Ana");
hashSet.add("Zara"); // Duplicado ignorado
hashSet.add("Pedro");
System.out.println("HashSet: " + hashSet);
// Salida probable: [Pedro, Ana, Zara] (Orden impredecible)
// 2. LinkedHashSet: Orden de llegada
Set<String> linkedSet = new LinkedHashSet<>();
linkedSet.add("Zara");
linkedSet.add("Ana");
linkedSet.add("Pedro");
System.out.println("LinkedHashSet: " + linkedSet);
// Salida garantizada: [Zara, Ana, Pedro]
// 3. TreeSet: Orden alfabético
Set<String> treeSet = new TreeSet<>();
treeSet.add("Zara");
treeSet.add("Ana");
treeSet.add("Pedro");
System.out.println("TreeSet: " + treeSet);
// Salida garantizada: [Ana, Pedro, Zara] (Ordenado!)
}
}
La trampa de los objetos propios (hashCode y equals)
Si usas Sets con String o Integer, todo funciona genial. Pero, ¿qué pasa si creas un Set<Usuario>?
Set<Usuario> usuarios = new HashSet<>();
usuarios.add(new Usuario("Pepe", "1234"));
usuarios.add(new Usuario("Pepe", "1234")); // ¿Es duplicado?
System.out.println(usuarios.size()); // ¡Imprime 2!
¿Por qué?
Para Java, dos objetos creados con new son distintos en memoria, aunque tengan los mismos datos dentro.
Para que un Set funcione correctamente con tus clases, DEBES sobrescribir los métodos equals() y hashCode() en tu clase Usuario.
equals(): Define cuándo dos usuarios son lógicamente iguales (ej: mismo DNI).hashCode(): Genera un número identificador basado en el DNI.
Contrato de igualdad:
Si metes tus propios objetos en un HashSet o HashMap, usa siempre la generación automática de tu IDE para crear equals y hashCode. Si no lo haces, la unicidad no funcionará.
Operaciones de conjuntos (matemáticas)
Los Sets son geniales para hacer operaciones de Diagramas de Venn de forma masiva:
Set<Integer> a = new HashSet<>(Arrays.asList(1, 2, 3));
Set<Integer> b = new HashSet<>(Arrays.asList(3, 4, 5));
// UNIÓN (A U B): Todo lo que está en A o en B
a.addAll(b); // a ahora es [1, 2, 3, 4, 5]
// INTERSECCIÓN (A ∩ B): Solo lo que está en ambos
a.retainAll(b); // a ahora sería [3]
// DIFERENCIA (A - B): Lo que está en A pero no en B
a.removeAll(b); // a ahora sería [1, 2]