Map хранит пары ключ-значение
HashMap (из java.util) хранит соответствия: каждый ключ сопоставлен с одним значением, и вы ищете значения по их ключу примерно за константное время. Представьте словарь: слово — это ключ, определение — это значение.
Два параметра типа — это <KeyType, ValueType>. Здесь ключи имеют тип String, а значения — Integer. Как и с ArrayList, переменную обычно объявляют с типом интерфейса Map, а создают HashMap.
put, get и перезапись
Две вещи, которые стоит усвоить:
- Ключ уникален.
putс существующим ключом заменяет его значение и возвращает старое. getпо отсутствующему ключу возвращаетnull, а не ошибку. Автораспаковка этогоnullвintвыбрасываетNullPointerException, что является частым источником ошибок.
getOrDefault избавляет от ловушки с null
Вместо того чтобы каждый раз проверять на null, запрашивайте значение по умолчанию:
Это самый чистый способ обработки поиска «возможно, есть», и он напрямую ведёт к самому известному приёму с HashMap.
Подсчёт вхождений
Подсчёт того, сколько раз встречается каждый элемент, — это хрестоматийная задача для HashMap:
Приём map.put(key, map.getOrDefault(key, 0) + 1) читается как «возьми текущий счётчик (или ноль), прибавь единицу и запиши обратно». Более аккуратный эквивалент — counts.merge(word, 1, Integer::sum).
Проверка и удаление
putIfAbsent(key, value) записывает значение только тогда, когда ключ отсутствует, — полезно для отложенной инициализации.
Перебор HashMap
Самый распространённый цикл проходит по entrySet(), давая вам ключ и значение вместе:
Если нужны только ключи или только значения:
Можно также использовать forEach с лямбдой: ages.forEach((name, age) -> System.out.println(name + ": " + age));.
HashMap не сохраняет порядок
HashMap не даёт никаких гарантий относительно порядка перебора: он определяется хешированием и может меняться от запуска к запуску. Если вам нужен предсказуемый порядок:
LinkedHashMapсохраняет порядок вставки.TreeMapдержит ключи отсортированными по естественному порядку (или поComparator, который вы предоставите).
Все три реализуют интерфейс Map, поэтому переход с одной на другую — это изменение одной строки в конструкторе.
Ключи должны быть хешируемыми
HashMap находит записи, хешируя ключ, поэтому методы hashCode() и equals() ключа должны быть согласованы. Встроенные типы вроде String и Integer уже делают это правильно. Если вы используете собственный класс в качестве ключа, переопределите и equals, и hashCode, иначе два объекта, «равные» по смыслу, попадут в разные корзины, и ваш поиск будет загадочным образом давать сбои.
Далее: HashSet
HashMap отвечает на вопрос «какое значение хранится под этим ключом?». Когда вам важно только то, присутствует ли что-то вообще (набор уникальных значений без связанных данных), нужный инструмент — HashSet, и он будет дальше.
Часто задаваемые вопросы
Как создать HashMap в Java?
Объявите её с двумя параметрами типа (тип ключа и тип значения) и вызовите конструктор: Map<String, Integer> ages = new HashMap<>();. Затем добавляйте записи через ages.put("Ada", 36); и читайте их через ages.get("Ada");. Импортируйте java.util.HashMap и java.util.Map.
Как пройти по HashMap в Java?
Перебирайте map.entrySet() циклом for-each, чтобы получать ключ и значение вместе: for (Map.Entry<String, Integer> e : map.entrySet()) { ... }, читая e.getKey() и e.getValue(). Можно также перебирать map.keySet(), чтобы получить только ключи, или map.values(), чтобы получить только значения. Учтите, что HashMap не сохраняет порядок вставки.
В чём разница между get и getOrDefault?
get(key) возвращает значение по ключу или null, если ключ отсутствует, что может привести к NullPointerException, если использовать результат напрямую. getOrDefault(key, fallback) возвращает значение, если оно есть, иначе переданное вами значение по умолчанию, так что проверка на null не нужна. Особенно удобно для подсчёта: counts.put(c, counts.getOrDefault(c, 0) + 1).