1. Introducción
En la vida real a menudo nos encontramos con situaciones en las que a cada «clave» única hay que asociarle algún «valor». Una agenda telefónica guarda el número de teléfono y el nombre de la persona, un diccionario relaciona una palabra con su traducción y, en una tabla de calificaciones, cada estudiante tiene su propio nombre y la puntuación correspondiente.
En Java, para estas tareas existe la interfaz Map. Es una colección que almacena pares «clave — valor» (key-value pair).
Propiedades principales de Map:
- Cada clave es única (no se permiten duplicados).
- A una única clave puede corresponder un solo valor.
- Los valores pueden repetirse.
Imaginemos una analogía. Si una lista (List) es como una cola en el comedor (cada uno está en su posición y puedes acceder por índice), un mapa (Map) es como un armario con casilleros: cada casillero tiene un número (clave) y dentro hay algo propio (valor).
Interfaz Map: operaciones básicas
La interfaz Map declara los métodos principales para trabajar con pares clave-valor:
| Método | Descripción |
|---|---|
|
Añadir/reemplazar el valor por clave |
|
Obtener el valor por clave |
|
Eliminar el par por clave |
|
Comprobar si existe esa clave |
|
Comprobar si existe ese valor |
|
Número de pares en el mapa |
|
Comprobar si el Map está vacío |
|
Eliminar todos los pares |
Los tipos K y V son parámetros genéricos: K (Key) es el tipo de la clave, V (Value) es el tipo del valor.
3. Clase HashMap: acceso rápido por clave
¿Qué es HashMap?
HashMap es la implementación más popular de la interfaz Map. Proporciona acceso rápido a los valores por clave.
Importante: HashMap no garantiza el orden de almacenamiento de los elementos. Si añadiste las claves en un orden determinado, al iterarlas pueden aparecer en otro diferente.
¿Cómo crear un HashMap?
import java.util.HashMap;
import java.util.Map;
public class Example {
public static void main(String[] args) {
// Creamos el mapa: clave — String, valor — Integer
Map<String, Integer> ages = new HashMap<>();
// Añadimos elementos
ages.put("Alice", 25);
ages.put("Bob", 30);
ages.put("Charlie", 22);
// Obtenemos el valor por clave
int vasyaAge = ages.get("Alice");
System.out.println("Edad de Alice: " + vasyaAge); // 25
// Comprobamos la existencia de la clave
if (ages.containsKey("Charlie")) {
System.out.println("¡Charlie está en la lista!");
}
// Eliminamos un elemento
ages.remove("Bob");
// Recorremos todos los pares clave-valor
for (String name : ages.keySet()) {
System.out.println(name + ": " + ages.get(name));
}
}
}
Salida:
Edad de Alice: 25
¡Charlie está en la lista!
Alice: 25
Charlie: 22
Particularidades de HashMap
Lo principal que debes recordar: las claves en HashMap siempre son únicas. Si insertas un nuevo elemento con una clave ya existente, el valor anterior se sustituirá por el nuevo.
Los valores pueden repetirse: varias claves distintas pueden apuntar al mismo valor.
Y otro punto importante: el orden de los elementos. HashMap no se preocupa por el orden de inserción. Al imprimir, las entradas pueden aparecer desordenadas — es un comportamiento normal.
4. Clase TreeMap: ordenación por clave
A diferencia de HashMap, la clase TreeMap almacena los elementos en orden, según la clave.
¿Cuándo usar TreeMap?
Cuando es importante que los elementos sigan el orden ascendente (o descendente) de las claves. Por ejemplo, si quieres imprimir una agenda telefónica en orden alfabético.
Ejemplo:
import java.util.Map;
import java.util.TreeMap;
public class TreeMapExample {
public static void main(String[] args) {
Map<String, String> phoneBook = new TreeMap<>();
phoneBook.put("Alice", "+1-900-123-45-67");
phoneBook.put("Charlie", "+1-900-555-55-55");
phoneBook.put("Bob", "+1-900-222-33-44");
for (String name : phoneBook.keySet()) {
System.out.println(name + ": " + phoneBook.get(name));
}
}
}
Salida:
Charlie: +1-900-555-55-55
Bob: +1-900-222-33-44
Alice: +1-900-123-45-67
Ten en cuenta: las claves están ordenadas alfabéticamente.
5. Operaciones básicas con Map
Añadir y sustituir elementos
Map<String, Integer> scores = new HashMap<>();
scores.put("Grace", 90);
scores.put("David", 85);
scores.put("Grace", 95); // Sobrescribirá el valor de "Grace"
Obtener un valor
Integer annaScore = scores.get("Grace"); // 95
Integer unknown = scores.get("Alice"); // null, si no existe esa clave
Comprobar existencia de clave o valor
scores.containsKey("David"); // true
scores.containsValue(85); // true
Eliminar un par por clave
scores.remove("David");
Tamaño del mapa y limpieza
int size = scores.size();
scores.clear(); // Elimina todos los elementos
5. Recorrer los elementos de un Map
Map no es una lista; aquí no hay índices. Pero se puede recorrer:
Por claves:
for (String key : scores.keySet()) {
System.out.println("Clave: " + key + ", Valor: " + scores.get(key));
}
Por valores:
for (Integer value : scores.values()) {
System.out.println("Valor: " + value);
}
Por pares clave-valor (la mejor opción):
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + " => " + value);
}
¿Cuándo usar HashMap y cuándo TreeMap?
HashMap es la opción «por defecto» y más versátil. Si el orden de las claves no importa y lo principal es la velocidad de las operaciones, casi siempre se usa.
TreeMap es útil si necesitas orden. Mantiene las claves ordenadas automáticamente y permite encontrar rápidamente la clave mínima/máxima o trabajar con rangos.
En resumen: en el 90% de los casos usamos HashMap. Cuando los datos deben estar «ya en orden», utilizamos TreeMap.
6. Ejemplos de uso de Map
Ejemplo 1: Agenda telefónica
Map<String, String> phoneBook = new HashMap<>();
phoneBook.put("Frank", "+1-999-111-22-33");
phoneBook.put("Eve", "+1-999-222-33-44");
phoneBook.put("Frank", "+1-999-555-66-77"); // El número antiguo de Frank se sustituirá por el nuevo
for (Map.Entry<String, String> entry : phoneBook.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
Salida:
Eve: +1-999-222-33-44
Frank: +1-999-555-66-77
Ejemplo 2: Recuento de palabras
Supongamos que tenemos una lista de palabras y queremos saber cuántas veces aparece cada una:
import java.util.*;
public class WordCount {
public static void main(String[] args) {
List<String> words = Arrays.asList("manzana", "plátano", "manzana", "pera", "plátano", "manzana");
Map<String, Integer> counts = new HashMap<>();
for (String word : words) {
int oldCount = counts.getOrDefault(word, 0); // si no existe la clave - 0
counts.put(word, oldCount + 1);
}
System.out.println(counts); // {pera=1, manzana=3, plátano=2}
}
}
7. Errores típicos al trabajar con Map
Error n.º 1: Confundir las claves con los valores. Los principiantes a menudo intentan obtener un valor por índice, como en una lista, o se olvidan de que las claves deben ser únicas. En Map no hay índices — solo claves.
Error n.º 2: Usar claves y valores null. En HashMap se permite la clave null, pero en TreeMap no (aparecerá NullPointerException). Los valores pueden ser null en ambas implementaciones, pero rara vez es útil.
Error n.º 3: Esperar un orden de los elementos en HashMap. HashMap no garantiza ningún orden. Si necesitas orden, usa LinkedHashMap (conserva el orden de inserción) o TreeMap (ordena por clave).
Error n.º 4: Modificar el Map durante la iteración. Si en un bucle recorres un Map y al mismo tiempo añades/eliminás elementos, puede producirse una ConcurrentModificationException. Para estos casos usa un iterador con el método remove() o colecciones especiales.
Error n.º 5: Comparar claves y valores con == en lugar de equals. Map utiliza el método equals para comparar claves (y valores). Si creas tus propias clases como claves, asegúrate de sobrescribir equals y hashCode.
GO TO FULL VERSION