Un conjunto almacena valores únicos
Un HashSet (de java.util) es una colección que guarda cada valor como máximo una vez. No hay claves ni valores emparejados como en un HashMap, solo un saco de elementos distintos. Su único cometido es responder rápido a una pregunta: "¿está esto aquí dentro?"
Hay un único parámetro de tipo, <ElementType>. Igual que con ArrayList y HashMap, normalmente declaras la variable con el tipo de la interfaz Set y construyes un HashSet.
add devuelve si el valor era nuevo
add no solo almacena el valor: devuelve un boolean que te indica si el conjunto cambió realmente. Añadir un valor que ya está presente devuelve false y deja el conjunto intacto.
Ese valor de retorno es realmente útil: if (!seen.add(x)) { /* x es un repetido */ } te permite detectar duplicados en una sola línea sobre la marcha.
Eliminar duplicados de una lista
Como un conjunto rechaza los repetidos, la forma más rápida de quitar duplicados de una colección es volcarla en uno. El constructor de HashSet acepta cualquier otra colección:
Esta es la razón más común por la que los principiantes recurren a un conjunto. Solo ten en cuenta que pierdes el orden original en la ida y vuelta: usa LinkedHashSet si el orden importa (lo vemos más abajo).
contains, remove y size
Las operaciones del día a día reflejan las de las otras colecciones:
La gran ventaja sobre un ArrayList es contains. Una lista tiene que recorrer todos los elementos para responder (O(n)); un HashSet llega casi directamente a la respuesta (aproximadamente O(1)). Cuando te descubras llamando a list.contains(...) dentro de un bucle, esa suele ser la señal para cambiar a un conjunto.
Operaciones de conjuntos: unión, intersección, diferencia
Los conjuntos brillan cuando los combinas. Los métodos se leen casi como lenguaje natural una vez que sabes cuál es cuál:
El detalle clave: addAll, retainAll y removeAll modifican el conjunto sobre el que se llaman. Por eso cada ejemplo copia a en un HashSet nuevo primero; de lo contrario destruirías tu original. Crea un conjunto nuevo para cada resultado.
HashSet no mantiene el orden
Igual que HashMap, un HashSet no garantiza ningún orden de iteración, y el orden puede variar entre ejecuciones. Si necesitas previsibilidad:
LinkedHashSetconserva el orden de inserción, el orden en que añadiste los elementos.TreeSetmantiene los elementos ordenados por su orden natural (o por unComparatorque tú proporciones).
Los tres implementan la interfaz Set, así que cambiar entre ellos es una modificación de una sola línea en el constructor.
Los elementos deben ser hashables
Un HashSet está respaldado internamente por un HashMap, así que se aplica la misma regla: localiza los elementos calculando su hash, lo que significa que el hashCode() y el equals() de un elemento deben ser coherentes. Los tipos integrados como String e Integer ya lo hacen bien, por eso las cadenas "java" duplicadas se fusionan correctamente arriba. Si almacenas instancias de tu propia clase, sobrescribe tanto equals como hashCode; de lo contrario, dos objetos que son "iguales" en significado se tratarán como distintos, y contains y la eliminación de duplicados fallarán en silencio.
Siguiente: Iterar colecciones
Ya has conocido las tres colecciones fundamentales: ArrayList, HashMap y HashSet. Cada una se recorre de forma un poco distinta, y hay trampas sutiles (como modificar una colección mientras iteras sobre ella). A continuación lo juntaremos todo y veremos cómo iterar colecciones de forma limpia con el bucle for-each, los iteradores y forEach.
Preguntas frecuentes
¿Cómo se crea un HashSet en Java?
Declíralo con un único parámetro de tipo (el tipo del elemento) y llama al constructor: Set<String> tags = new HashSet<>();. Añade valores con tags.add("java"); y comprueba la pertenencia con tags.contains("java");. Importa java.util.HashSet y java.util.Set.
¿Cuál es la diferencia entre un HashSet y un ArrayList en Java?
Un ArrayList conserva todos los elementos que añades (incluidos los duplicados) en orden de inserción y se accede a ellos por posición. Un HashSet almacena solo valores únicos, no garantiza ningún orden, no tiene índice y su comprobación contains es aproximadamente de tiempo constante en lugar de recorrer toda la lista. Usa un HashSet cuando te importe la unicidad o la pertenencia rápida, no la posición.
¿Cómo se eliminan los duplicados de una lista en Java?
Pasa la lista al constructor de un HashSet: Set<String> unique = new HashSet<>(list);. El conjunto descarta los valores repetidos automáticamente. Si necesitas recuperar una lista (y no te importa perder el orden), vuelve a envolverlo: new ArrayList<>(unique). Usa un LinkedHashSet si quieres conservar el orden original.