Un Map es una estructura de datos para almacenar pares clave-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 distintas de Map. HashMap es una de las más usadas.
// Make an instance
Map<String, Integer> fruitPrices = new HashMap<>();
Cuando defines una variable de tipo Map, se recomienda declararla como 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);
Agrega entradas al Map con put.
fruitPrices.put("apple", 100);
fruitPrices.put("pear", 80);
// => { "apple" => 100, "pear" => 80 }
Solo un valor puede estar asociado a cada clave.
Si llamas a put con la misma clave, se actualiza 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 ver si el Map contiene una clave en particular.
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 y cómo recuperar los valores de una clave. Por esta razón, es importante que sus valores de retorno no cambien entre el momento de almacenarlos y el de recuperarlos, ya que de lo contrario el HashMap podría no encontrar 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 a través de setName.
Modificar el hashCode puede producir 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 común de crear Map 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, llenan el Map de antemano y quedan 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.