Une Map est une structure de données qui permet de stocker des paires clé-valeur. Elle est similaire aux dictionnaires d'autres langages de programmation. L'interface Map définit les opérations que l'on peut effectuer sur une Map.
Java propose plusieurs implémentations de Map. HashMap est l'une des plus courantes.
// Make an instance
Map<String, Integer> fruitPrices = new HashMap<>();
Lorsque tu définis une variable Map, il est recommandé de lui donner le type Map plutôt que le type spécifique, comme dans l'exemple ci-dessus.
Cette pratique facilite le changement d'implémentation de la Map par la suite.
HashMap possède aussi un constructeur de copie.
// Make a copy of a map
Map<String, Integer> copy = new HashMap<>(fruitPrices);
Ajoute des entrées à la Map avec put.
fruitPrices.put("apple", 100);
fruitPrices.put("pear", 80);
// => { "apple" => 100, "pear" => 80 }
Une seule valeur peut être associée à chaque clé.
Appeler put avec la même clé met à jour la valeur de cette clé.
fruitPrices.put("pear", 40);
// => { "apple" => 100, "pear" => 40 }
Utilise get pour obtenir la valeur associée à une clé.
fruitPrices.get("apple"); // => 100
Utilise containsKey pour savoir si la Map contient une clé donnée.
fruitPrices.containsKey("apple"); // => true
fruitPrices.containsKey("orange"); // => false
Supprime des entrées avec remove.
fruitPrices.put("plum", 90); // Add plum to map
fruitPrices.remove("plum"); // Removes plum from map
La méthode size renvoie le nombre d'entrées.
fruitPrices.size(); // Returns 2
Tu peux utiliser les méthodes keySet ou values pour obtenir les clés ou les valeurs d'une Map sous la forme d'un Set ou d'une collection, respectivement.
fruitPrices.keySet(); // Returns "apple" and "pear" in a set
fruitPrices.values(); // Returns 100 and 80, in a Collection
hashCode et equals
Les HashMap utilisent les méthodes hashCode et equals d'un objet pour déterminer où stocker les valeurs associées à une clé et comment les récupérer. Pour cette raison, il est important que leurs valeurs de retour ne changent pas entre le stockage et la récupération, sinon la HashMap risque de ne pas retrouver la valeur.
Par exemple, imaginons que l'on ait la classe suivante, destinée à servir de clé dans une 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;
}
}
Les méthodes hashCode et equals dépendent du champ name, qui peut être modifié via setName.
Modifier le hashCode peut donner des résultats surprenants :
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
Une autre façon courante de créer des maps consiste à utiliser Map.of ou 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)
);
Contrairement à HashMap, elles sont remplies dès le départ et deviennent en lecture seule une fois créées.
Map.copyOf crée une copie en lecture seule d'une Map.
Map<String, Integer> readOnlyFruitPrices = Map.copyOf(fruitPrices);
Appeler des méthodes comme put, remove ou clear lève une UnsupportedOperationException.