Мапа - це структура даних для зберігання пар ключ-значення. Вона подібна до словників в інших мовах програмування. Інтерфейс Map визначає операції, які можна виконувати з мапою.
У Java є чимало різних реалізацій Map. HashMap - одна з найпоширеніших.
// Make an instance
Map<String, Integer> fruitPrices = new HashMap<>();
Коли ми оголошуємо змінну Map, краще вказувати тип Map, а не конкретний тип, як у прикладі вище.
Так згодом легше змінити реалізацію Map.
HashMap також має конструктор копіювання.
// Make a copy of a map
Map<String, Integer> copy = new HashMap<>(fruitPrices);
Щоб додати записи до мапи, скористаймося put.
fruitPrices.put("apple", 100);
fruitPrices.put("pear", 80);
// => { "apple" => 100, "pear" => 80 }
З кожним ключем може бути повʼязане лише одне значення.
Якщо викликати put із тим самим ключем, значення цього ключа оновиться.
fruitPrices.put("pear", 40);
// => { "apple" => 100, "pear" => 40 }
Щоб отримати значення для ключа, скористаймося get.
fruitPrices.get("apple"); // => 100
Щоб перевірити, чи містить мапа певний ключ, скористаймося containsKey.
fruitPrices.containsKey("apple"); // => true
fruitPrices.containsKey("orange"); // => false
Щоб видалити записи, скористаймося remove.
fruitPrices.put("plum", 90); // Add plum to map
fruitPrices.remove("plum"); // Removes plum from map
Метод size повертає кількість записів.
fruitPrices.size(); // Returns 2
Методи keySet і values дають змогу отримати ключі або значення мапи як Set чи колекцію відповідно.
fruitPrices.keySet(); // Returns "apple" and "pear" in a set
fruitPrices.values(); // Returns 100 and 80, in a Collection
hashCode та equals
HashMap використовує методи hashCode та equals обʼєкта, щоб визначити, де зберігати значення для ключа і як їх діставати. Тому важливо, щоб їхні повернені значення не змінювалися між збереженням і отриманням, інакше HashMap може не знайти значення.
Наприклад, припустімо, у нас є такий клас, який буде використовуватися як ключ мапи:
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;
}
}
Методи hashCode та equals залежать від поля name, яке можна змінити через setName.
Зміна hashCode може дати несподівані результати:
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
Ще один поширений спосіб створити мапу - скористатися Map.of або 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)
);
На відміну від HashMap, вони заповнюють мапу одразу й після створення стають доступними лише для читання.
Map.copyOf створює копію мапи, доступну лише для читання.
Map<String, Integer> readOnlyFruitPrices = Map.copyOf(fruitPrices);
Виклик таких методів, як put, remove чи clear, призводить до UnsupportedOperationException.