Un Map es una estructura de datos que almacena pares de clave y valor. Es similar a los diccionarios de otros lenguajes de programación. La interfaz Map define las operaciones que puedes realizar con un Map.
Java tiene varias implementaciones de Map diferentes. HashMap es una de las más utilizadas.
// Make an instance
Map<String, Integer> fruitPrices = new HashMap<>();
Al definir una variable Map, se recomienda declararla con el tipo Map en lugar del tipo específico, como en el ejemplo anterior.
Esta práctica facilita cambiar la implementación de Map más adelante.
HashMap también tiene un constructor de copia.
// Make a copy of a map
Map<String, Integer> copy = new HashMap<>(fruitPrices);
Añade entradas al Map con put.
fruitPrices.put("apple", 100);
fruitPrices.put("pear", 80);
// => { "apple" => 100, "pear" => 80 }
Solo se puede asociar un valor a cada clave.
Si llamas a put con la misma clave, se actualizará el valor de esa clave.
fruitPrices.put("pear", 40);
// => { "apple" => 100, "pear" => 40 }
Usa get para obtener el valor de una clave.
fruitPrices.get("apple"); // => 100
Usa containsKey para comprobar si el Map contiene una clave concreta.
fruitPrices.containsKey("apple"); // => true
fruitPrices.containsKey("orange"); // => false
Elimina entradas con remove.
fruitPrices.put("plum", 90); // Add plum to map
fruitPrices.remove("plum"); // Removes plum from map
El método size devuelve el número de entradas.
fruitPrices.size(); // Returns 2
Puedes usar los métodos keySet o values para obtener las claves o los valores de un Map como un Set o una colección, respectivamente.
fruitPrices.keySet(); // Returns "apple" and "pear" in a set
fruitPrices.values(); // Returns 100 and 80, in a Collection
hashCode y equals
HashMap usa los métodos hashCode y equals del objeto para determinar dónde almacenar los valores de una clave y cómo recuperarlos. Por este motivo, es importante que sus valores devueltos no cambien entre el momento de almacenarlos y el de recuperarlos; de lo contrario, es posible que el HashMap no encuentre el valor.
Por ejemplo, supongamos que tenemos la siguiente clase que se usará como clave de un Map:
public class Stock {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
return Objects.hash(name);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (Objects.equals(Stock.class, obj.getClass()) && obj instanceof Stock other) {
return Objects.equals(name, other.name);
}
return false;
}
}
Los métodos hashCode y equals dependen del campo name, que se puede cambiar mediante setName.
Modificar el hashCode puede dar resultados sorprendentes:
Stock stock = new Stock();
stock.setName("Beanies");
Map<Stock, Integer> stockCount = new HashMap<>();
stockCount.put(stock, 80);
stockCount.get(stock); // Returns 80
Stock other = new Stock();
other.setName("Beanies");
stockCount.get(other); // Returns 80 because "other" and "stock" are equal
stock.setName("Choccies");
stockCount.get(stock); // Returns null because hashCode value has changed
stockCount.get(other); // Also returns null because "other" and "stock" are not equal
stock.setName("Beanies");
stockCount.get(stock); // HashCode restored, so returns 80 again
stockCount.get(other); // Also returns 80 again because "other" and "stock" are back to equal
Otra forma habitual de crear Maps es usar Map.of o Map.ofEntries.
// Using Map.of
Map<String, Integer> temperatures = Map.of("Mon", 30, "Tue", 28, "Wed", 32);
// or using Map.ofEntries
Map<String, Integer> temperatures2 = Map.ofEntries(
Map.entry("Mon", 30),
Map.entry("Tue", 28),
Map.entry("Wed", 32)
);
A diferencia de HashMap, rellenan el Map de antemano y pasan a ser de solo lectura una vez creados.
Map.copyOf crea una copia de solo lectura de un Map.
Map<String, Integer> readOnlyFruitPrices = Map.copyOf(fruitPrices);
Llamar a métodos como put, remove o clear produce una UnsupportedOperationException.