1. Введение
В реальной жизни мы часто сталкиваемся с ситуациями, когда каждому уникальному «ключу» нужно сопоставить какое-то «значение». Телефонная книга хранит номер телефона и имя человека, словарь связывает слово с его переводом, а в таблице оценок у каждого студента есть своё имя и соответствующий балл.
В Java для таких задач есть интерфейс Map. Это коллекция, которая хранит пары «ключ — значение» (key-value pair).
Главные свойства Map:
- Каждый ключ уникален (дубликаты не допускаются).
- Одному ключу может соответствовать только одно значение.
- Значения могут повторяться.
Представим аналогию. Если список (List) — это как очередь в столовой (каждый стоит на своей позиции, можно обратиться по номеру), то отображение (Map) — это как шкафчик с ячейками: у каждой ячейки есть номер (ключ), и в ней лежит что-то своё (значение).
Интерфейс Map: базовые операции
Интерфейс Map объявляет основные методы для работы с парами ключ-значение:
| Метод | Описание |
|---|---|
|
Добавить/заменить значение по ключу |
|
Получить значение по ключу |
|
Удалить пару по ключу |
|
Проверить, есть ли такой ключ |
|
Проверить, есть ли такое значение |
|
Количество пар в отображении |
|
Проверить, пустой ли Map |
|
Удалить все пары |
Типы K и V — это generic-параметры: K (Key) — тип ключа, V (Value) — тип значения.
3. Класс HashMap: быстрый доступ по ключу
Что такое HashMap?
HashMap — самая популярная реализация интерфейса Map. Она обеспечивает быстрый доступ к значениям по ключу.
Важно: HashMap не гарантирует порядок хранения элементов! Если вы добавили ключи в определённом порядке, при переборе они могут идти в другом.
Как создать HashMap?
import java.util.HashMap;
import java.util.Map;
public class Example {
public static void main(String[] args) {
// Создаём карту: ключ — String, значение — Integer
Map<String, Integer> ages = new HashMap<>();
// Добавляем элементы
ages.put("Вася", 25);
ages.put("Петя", 30);
ages.put("Маша", 22);
// Получаем значение по ключу
int vasyaAge = ages.get("Вася");
System.out.println("Возраст Васи: " + vasyaAge); // 25
// Проверяем наличие ключа
if (ages.containsKey("Маша")) {
System.out.println("Маша есть в списке!");
}
// Удаляем элемент
ages.remove("Петя");
// Перебираем все пары ключ-значение
for (String name : ages.keySet()) {
System.out.println(name + ": " + ages.get(name));
}
}
}
Вывод:
Возраст Васи: 25
Маша есть в списке!
Вася: 25
Маша: 22
Особенности HashMap
Главное, что стоит помнить: ключи в HashMap всегда уникальны. Если вы положите новый элемент с уже существующим ключом, старое значение будет заменено новым.
Значения могут повторяться: несколько разных ключей могут указывать на одно и то же значение.
И ещё важный момент — порядок элементов. HashMap не заботится о порядке добавления. При выводе записи могут быть перемешаны — это нормальное поведение.
4. Класс TreeMap: сортировка по ключу
В отличие от HashMap, класс TreeMap хранит элементы в отсортированном порядке по ключу.
Когда использовать TreeMap?
Когда важно, чтобы элементы шли по возрастанию (или убыванию) ключей. Например, если вы хотите вывести телефонную книгу по алфавиту.
Пример:
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("Вася", "+1-900-123-45-67");
phoneBook.put("Маша", "+1-900-555-55-55");
phoneBook.put("Петя", "+1-900-222-33-44");
for (String name : phoneBook.keySet()) {
System.out.println(name + ": " + phoneBook.get(name));
}
}
}
Вывод:
Маша: +1-900-555-55-55
Петя: +1-900-222-33-44
Вася: +1-900-123-45-67
Обратите внимание: ключи отсортированы по алфавиту.
5. Основные операции с Map
Добавление и замена элементов
Map<String, Integer> scores = new HashMap<>();
scores.put("Анна", 90);
scores.put("Иван", 85);
scores.put("Анна", 95); // Перезапишет значение для "Анна"
Получение значения
Integer annaScore = scores.get("Анна"); // 95
Integer unknown = scores.get("Вася"); // null, если такого ключа нет
Проверка наличия ключа или значения
scores.containsKey("Иван"); // true
scores.containsValue(85); // true
Удаление пары по ключу
scores.remove("Иван");
Размер карты и очистка
int size = scores.size();
scores.clear(); // Удаляет все элементы
5. Перебор элементов Map
Map — это не список, тут нет индексов. Но можно перебирать:
По ключам:
for (String key : scores.keySet()) {
System.out.println("Ключ: " + key + ", Значение: " + scores.get(key));
}
По значениям:
for (Integer value : scores.values()) {
System.out.println("Значение: " + value);
}
По парам ключ-значение (лучший способ):
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + " => " + value);
}
Когда использовать HashMap, а когда — TreeMap?
HashMap — универсальный вариант «по умолчанию». Если порядок ключей неважен и главное — скорость операций, почти всегда берут именно его.
TreeMap пригодится, если нужен порядок. Он автоматически хранит ключи отсортированными и позволяет быстро находить минимальный/максимальный ключ или работать с диапазонами.
Итого: в 90% случаев берём HashMap. Когда данные должны быть сразу «в порядке», используем TreeMap.
6. Примеры использования Map
Пример 1: Телефонная книга
Map<String, String> phoneBook = new HashMap<>();
phoneBook.put("Катя", "+1-999-111-22-33");
phoneBook.put("Олег", "+1-999-222-33-44");
phoneBook.put("Катя", "+1-999-555-66-77"); // Старый номер Кати заменится новым
for (Map.Entry<String, String> entry : phoneBook.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
Вывод:
Олег: +1-999-222-33-44
Катя: +1-999-555-66-77
Пример 2: Подсчёт количества слов
Допустим, у нас есть список слов, и мы хотим узнать, сколько раз каждое слово встречается:
import java.util.*;
public class WordCount {
public static void main(String[] args) {
List<String> words = Arrays.asList("яблоко", "банан", "яблоко", "груша", "банан", "яблоко");
Map<String, Integer> counts = new HashMap<>();
for (String word : words) {
int oldCount = counts.getOrDefault(word, 0); // если ключа нет — 0
counts.put(word, oldCount + 1);
}
System.out.println(counts); // {груша=1, яблоко=3, банан=2}
}
}
7. Типичные ошибки при работе с Map
Ошибка №1: Путаница между ключами и значениями. Новички часто пытаются получить значение по индексу, как в списке, или забывают, что ключи должны быть уникальными. В Map нет индексов — только ключи.
Ошибка №2: Использование null-ключей и значений. В HashMap допускается ключ null, но в TreeMap — нет (будет NullPointerException). Значения могут быть null в обеих реализациях, но это редко полезно.
Ошибка №3: Ожидание порядка элементов в HashMap. HashMap не гарантирует никакого порядка. Если нужен порядок — используйте LinkedHashMap (сохраняет порядок добавления) или TreeMap (сортирует по ключу).
Ошибка №4: Модификация Map во время перебора. Если вы в цикле перебираете Map и одновременно добавляете/удаляете элементы — может возникнуть ConcurrentModificationException. Для таких задач используйте итератор с методом remove() или специальные коллекции.
Ошибка №5: Сравнение ключей и значений по == вместо equals. Map использует метод equals для сравнения ключей (и значений). Если вы создаёте свои классы-ключи, обязательно переопределяйте equals и hashCode.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ