Set y Map
Una lista conserva una secuencia, incluso con duplicados. Otros problemas piden garantías distintas: etiquetas únicas o productos localizados por código.
Set: unicidad
Set<String> etiquetas = new HashSet<>();
etiquetas.add("java");
etiquetas.add("docker");
System.out.println(etiquetas.add("java")); // false
HashSet rechaza duplicados y no garantiza orden. Ofrece add, contains, remove y size. Se recorre sin índices. Elige Set cuando la unicidad sea parte del modelo; si necesitas posiciones o duplicados, usa List.
Map: clave y valor
Map<String, Integer> stock = new HashMap<>();
stock.put("T-01", 4);
stock.put("R-02", 7);
stock.put("T-01", 5);
La clave es única: el segundo put para T-01 sustituye el valor anterior. Claves y valores pueden tener tipos diferentes. get, remove, containsKey, getOrDefault y size cubren las operaciones habituales.
HashMap tampoco garantiza orden. Map no extiende Collection: almacena asociaciones, no elementos aislados.
Recorrer un Map
for (String codigo : stock.keySet()) System.out.println(codigo);
for (Integer cantidad : stock.values()) System.out.println(cantidad);
for (Map.Entry<String, Integer> entrada : stock.entrySet()) {
System.out.println(entrada.getKey() + ": " + entrada.getValue());
}
entrySet() resulta adecuado cuando necesitamos clave y valor juntos.
Igualdad en estructuras hash
HashSet y las claves de HashMap usan hashCode() para localizar candidatos y equals() para confirmar la igualdad. Con objetos propios debemos expresar la identidad lógica.
import java.util.Objects;
class Producto {
private final String codigo;
private String nombre;
public Producto(String codigo, String nombre) {
this.codigo = codigo;
this.nombre = nombre;
}
@Override
public boolean equals(Object otro) {
if (this == otro) return true;
if (!(otro instanceof Producto)) return false;
Producto producto = (Producto) otro;
return Objects.equals(codigo, producto.codigo);
}
@Override
public int hashCode() {
return Objects.hash(codigo);
}
}
Dos productos con el mismo código representan la misma identidad lógica. Regla práctica: si sobrescribes equals, sobrescribe también hashCode de forma coherente. Evita cambiar mientras está almacenado el dato que participa en ambos.
Ejemplo completo
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
class Producto {
private final String codigo;
private final String nombre;
public Producto(String codigo, String nombre) { this.codigo = codigo; this.nombre = nombre; }
@Override public boolean equals(Object otro) {
if (this == otro) return true;
if (!(otro instanceof Producto)) return false;
Producto p = (Producto) otro;
return Objects.equals(codigo, p.codigo);
}
@Override public int hashCode() { return Objects.hash(codigo); }
@Override public String toString() { return codigo + " - " + nombre; }
}
public class SetMapDemo {
public static void main(String[] args) {
Set<Producto> productos = new HashSet<>();
System.out.println(productos.add(new Producto("T-01", "Teclado")));
System.out.println(productos.add(new Producto("T-01", "Otro teclado")));
Map<String, Integer> stock = new HashMap<>();
stock.put("T-01", 4);
stock.put("T-01", 5);
for (Map.Entry<String, Integer> entrada : stock.entrySet()) {
System.out.println(entrada.getKey() + ": " + entrada.getValue());
}
}
}
Errores habituales
- Usar
Listy comprobar duplicados manualmente cuando la intención es unicidad. - Esperar orden de
HashSetoHashMap. - Sobrescribir
equalssinhashCode. - Usar como clave un dato mutable que participa en la igualdad.
- Confundir una clave ausente con un valor presente igual a
null.
Practica: recuento de votos
Enunciado y criterios
Cuenta votos por opción con Map<String, Integer> usando getOrDefault. Conserva también los nombres de votantes únicos en un Set.
Solución
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class VotosDemo {
public static void main(String[] args) {
String[] opciones = {"A", "B", "A", "A"};
Map<String, Integer> votos = new HashMap<>();
for (String opcion : opciones) {
votos.put(opcion, votos.getOrDefault(opcion, 0) + 1);
}
Set<String> votantes = new HashSet<>();
votantes.add("ana");
votantes.add("ana");
votantes.add("leo");
for (Map.Entry<String, Integer> entrada : votos.entrySet()) {
System.out.println(entrada.getKey() + ": " + entrada.getValue());
}
System.out.println("Votantes únicos: " + votantes.size());
}
}
Cada estructura expresa una garantía diferente: Map asocia opción y contador; Set asegura unicidad.