Un Map es una estructura que asocia claves únicas con valores.
Hasta ahora, nuestras colecciones (List, Set) almacenaban elementos individuales.
Pero en la vida real, los datos raramente van solos. Suelen ir asociados.
- Un DNI está asociado a una Persona.
- Un nombre de producto está asociado a un Precio.
- Un ticket de guardarropa está asociado a un Abrigo.
Si usas una List<Persona>, para encontrar a alguien por su DNI puedes necesitar recorrerla entera, con coste O(n).
Si el DNI es la clave, un mapa permite búsquedas más eficientes.
¿Qué es un mapa (Map)?
Un Mapa es un objeto que asocia Claves (Keys) con Valores (Values).
- Claves (
K): Deben ser Únicas (como unSet). No puede haber dos DNIs iguales. - Valores (
V): Pueden repetirse. Muchas personas pueden llamarse “Pepe”.
En un HashMap, buscar por clave tiene coste O(1) promedio, aunque las colisiones y la distribución de los hashes influyen en el caso real.
Map NO hereda de Collection. Es una interfaz independiente. Por eso no tiene método add(), sino put().
Las tres implementaciones (el mismo patrón)
Al igual que con los Sets, tenemos tres sabores dependiendo de si nos importa el orden o la velocidad.
Es la implementación por defecto. Úsala siempre a menos que tengas una razón para no hacerlo.
- Orden: Caótico. Las claves no siguen ningún orden.
- Velocidad: Rapidísimo.
- Claves: Usa
hashCode()para organizar los datos.
- Orden: Mantiene el orden de inserción. Si insertas la clave “Z”, luego “A”, al recorrerlo saldrán en ese orden.
- Uso: Cachés LRU (Least Recently Used) o interfaces donde el orden visual importa.
- Orden: Ordena las claves por su orden natural (alfabético o numérico).
- Velocidad: Más lento (O(logn)) porque tiene que reequilibrar el árbol cada vez que insertas algo.
Operaciones básicas
Vamos a crear una agenda telefónica. Clave: Nombre (String), Valor: Número (Integer).
// K = String, V = Integer
Map<String, Integer> agenda = new HashMap<>();
// 1. Insertar (PUT)
agenda.put("Ana", 666111222);
agenda.put("Bernardo", 666333444);
agenda.put("Ana", 123456789); // ¡OJO! Si la clave existe, SOBRESCRIBE el valor.
// 2. Recuperar (GET)
Integer numero = agenda.get("Bernardo"); // Devuelve 666333444
Integer desconocido = agenda.get("Fantasma"); // Devuelve null
// 3. Comprobar existencia
if (agenda.containsKey("Ana")) {
System.out.println("Ana está en la agenda");
}
// 4. Truco moderno (Java 8+): Get Or Default
// Evita el null pointer si no existe
Integer n = agenda.getOrDefault("Fantasma", 0); // Devuelve 0 si no lo encuentra
Cómo recorrer un mapa (iterar)
Esta es la duda nº 1 de los novatos. Como un Mapa no es una Lista, no puedes hacer for (Map m : mapa). Tienes 3 formas de recorrerlo:
Opción a: recorrer solo las claves (keySet)
Si solo te interesan los nombres.
for (String nombre : agenda.keySet()) {
System.out.println("Clave: " + nombre);
}
Opción b: recorrer solo los valores (values)
Si solo te interesan los números (ojo, puede haber repetidos).
for (Integer telefono : agenda.values()) {
System.out.println("Numero: " + telefono);
}
Opción c: recorrer todo (entrySet) - ¡la mejor!
Es la más eficiente si necesitas tanto la clave como el valor.
// Map.Entry representa la pareja (Clave + Valor)
for (Map.Entry<String, Integer> entrada : agenda.entrySet()) {
String k = entrada.getKey();
Integer v = entrada.getValue();
System.out.println(k + " -> " + v);
}
La trampa de la clave mutable
Hay una regla sagrada en los mapas: Las claves deben ser inmutables.
Si usas un objeto propio como Clave (ej: Map<Usuario, Factura>), y modificas un atributo del Usuario que afecte a su hashCode, el Mapa perderá la referencia.
Usuario u = new Usuario("Pepe");
mapa.put(u, factura);
u.setNombre("Jose"); // ¡PELIGRO! El hash ha cambiado.
mapa.get(u); // Probablemente devuelva null. El mapa busca a Pepe, no a Jose.
Por eso String e Integer son las mejores claves: porque son inmutables.