HashMap в Java: что это такое и зачем нужно
Это как словарь, только лучше. Рассказываем и показываем, как работать с «мэпами» в «кофейном» языке.


Иллюстрация: Оля Ежак для Skillbox Media
Хранить данные в Java можно по-разному. Кто-то записывает их в массив, кто-то создаёт сотни переменных… А кто-то складывает всё в хешмэпы, чтобы потом в доли секунды проводить любые операции с данными.
Сегодня мы узнаем, что такое HashMap, в чём его фишка, как его создать и какие методы для работы с ним есть в Java. В конце статьи есть шпаргалка, которую можно сохранить и держать под рукой во время занятий.
Из этой статьи вы узнаете:
- Что такое HashMap в Java
- Как его создать
- Как добавить элементы в HashMap
- Как получить и удалить элементы из HashMap
- Как узнать размер HashMap
- Как менять размер HashMap: методы resize и transfer
- Шпаргалка: как работать с HashMap
Что такое HashMap в Java
HashMap, или хеш-таблица, — это одна из структур данных в Java, и она чем-то похожа на обычный толковый словарь. Здесь у каждого слова есть своё определение, а количество этих слов ограничено только «лексиконом» говорящего. Однако в программировании используются немного другие обозначения: слова называются ключами, а определения — значениями.
Ключ — это как закладка в книге, по которой легко найти нужную страницу. А значение — это содержимое страницы книги.

Инфографика: Майя Мальгина для Skillbox Media
Ключами могут быть не только строки, а вообще любые данные: числа, объекты, нумерация, булевы переменные. Главное — чтобы они были уникальными. А для значений вообще нет никаких ограничений: они даже могут повторяться для разных ключей.
☝️Скорость работы — это главное преимущество хеш-таблиц перед массивами и другими структурами данных. Если знаем ключ, можем моментально получить значение, не перебирая все данные в словаре.
Смотрите:
- В массиве все элементы хранятся последовательно, а значит, чтобы найти нужный, компьютеру приходится сначала перебрать все предыдущие. Если массив большой — процесс займёт много времени.
- В хеш-таблице у нас есть указатель на конкретное место в памяти — ключ. По нему можно сразу «перепрыгнуть» к нужным данным и использовать их для своих целей.
Хеш-таблица так называется, потому что у каждого элемента в ней есть хеш. Хеш — это числовой номер, который показывает, в какое место таблицы нужно добавить новый элемент. Для вычисления хеша используют разные хеш-функции: можно использовать стандартные, например, метод hashCode(), а можно разработать свою.
Создадим вымышленную функцию, которая будет вычислять размер элементов в битах и делить их на какое-нибудь число — например, на 16.

Инфографика: Майя Мальгина для Skillbox Media
Может возникнуть вопрос: а что если у двух элементов получится одинаковый хеш? Например, если следовать принципу из картинки выше, хеш для чисел 674 и 690, будет одинаковым — 2. В какую ячейку тогда запишутся данные?
Отвечаем: на этот случай в HashMap есть система антиколлизий. Коллизия — это как раз тот случай, когда два элемента хотят записаться в одно и то же место. Чтобы избежать коллизий, используют такую схему:
- если место в памяти свободно — записать данные;
- если место занято — запомнить, что здесь есть данные, и сделать в этом месте цепочку данных.
Цепочка данных — это связный список. Старые данные находятся в начале списка, а новые прикрепляются к его хвосту. Получается такая связка:

Инфографика: Майя Мальгина для Skillbox Media
Если хеш нового элемента совпадёт с хешем уже существующего, то он автоматически попадёт в хвост. Очень простое решение, но у него есть и недостатки — например, хвост может так сильно разрастись, что придётся либо выделять для него много места в памяти, либо придумывать новую хеш-функцию, которая будет работать более эффективно.
Ещё одна особенность хеш-таблиц в том, что, когда мы создаём HashMap, он по умолчанию выделяет место под 16 ячеек. Неважно, заполним мы их сразу или позже. Главное — чтобы было место под новые элементы.
Как только количество элементов достигнет 75% от количества ячеек, HashMap увеличит свой размер в два раза. То есть максимальный размер станет уже 32. При этом изначальный размер HashMap можно задавать вручную, как и коэффициент загрузки, — то есть степень заполнения, при которой хеш-таблица будет создавать новые ячейки.
Давайте теперь познакомимся с основными методами для работы с HashMap. Узнаем, как создавать хеш-таблицы, менять их, удалять элементы и другое.
Как создать HashMap в Java
В качестве примера давайте создадим простую хеш-таблицу с номерами телефонов и их владельцами. При этом номера сделаем ключами, а имена — значениями. Всё потому, что последние могут повторяться, а первые всегда уникальные.
Чтобы создать HashMap, сначала нужно импортировать библиотеку:
import java.util.HashMap;
Теперь создаём HashMap как обычный класс:
HashMap numbersAndNames = new HashMap();
Но этого недостаточно, так как Java — строго типизированный язык. Это значит, что нам нужно явно указать типы переменных: и для ключей, и для значений. Делается это при помощи треугольных скобок:
HashMap<Integer, String> numbersAndNames = new HashMap<>();
Обратите внимание, что мы также добавили пару треугольных скобок при создании класса. Скобки справа — это как бы ещё одно указание, какие типы будут у ключей и значений, а возьмёт Java эти типы из левой части выражения.
Теперь давайте создадим пару элементов внутри словаря.
Как добавить элементы в HashMap
Чтобы добавить элемент в HashMap, используют метод put:
HashMap<Integer, String> numbersAndNames = new HashMap<>();
numbersAndNames.put(1234567, "Андрей");
System.out.println(numbersAndNames);
Результат:
{1234567=Андрей}
Метод put принимает два аргумента — сначала ключ, а потом значение. В нашем случае ключом был импровизированный номер телефона 1234567, а значением — строка с именем Андрей.
Давайте добавим ещё пару элементов:
HashMap<Integer, String> numbersAndNames = new HashMap<>();
numbersAndNames.put(1234567, "Андрей");
numbersAndNames.put(1333567, "Денис");
numbersAndNames.put(1266667, "Катя");
System.out.println(numbersAndNames);
Результат:
{1234567=Андрей, 1266667=Катя, 1333567=Денис}
Видим, что новые элементы добавились в словарь, но в странном порядке. У нас поменялись местами номера Дениса и Кати, хотя мы явно указали, в каком порядке они должны идти. Дело в том, что в HashMap используют хеши, чтобы находить места для записи данных, о чём мы рассказывали чуть выше.
Давайте попробуем добавить новый элемент с уже существующим номером и новым именем. Посмотрим, что получится:
numbersAndNames.put(1234567, "Андрей");
numbersAndNames.put(1333567, "Денис");
numbersAndNames.put(1333567, "Маша");
System.out.println(numbersAndNames);
Результат:
{1234567=Андрей, 1333567=Маша}
Видим, что второй элемент перезаписал первый. И теперь уже никак не узнать, что до это номер принадлежал Денису — теперь по нему можно дозвониться только до Маши. Поэтому нужно быть аккуратными, когда добавляете новый элемент по уже существующему ключу, — есть риск потерять данные.
Чтобы проверить, есть ли какие-то данные по ключу, применяют метод containsKey:
System.out.println(numbersAndNames.containsKey(1333567));
Результат:
true
Можем сделать простую проверку, перед тем как добавлять новые элементы с помощью условного оператора if: если такого ключа не существует, то спокойно добавляем:
numbersAndNames.put(1234567, "Андрей");
numbersAndNames.put(1333567, "Денис");
// Условная конструкция, которая проверяет наличие ключа
if (!numbersAndNames.containsKey(1333567)) {
numbersAndNames.put(1333567, "Маша");
}
Теперь перезаписать существующий ключ уже не получится.
Ещё один способ проверить, существует ли ключ, — через его значение. Для этого используют метод containsValue:
numbersAndNames.put(1234567, "Андрей");
numbersAndNames.put(1333567, "Денис");
System.out.println(numbersAndNames.containsValue("Андрей"));
Результат:
true
Переходим к следующей важной теме — попробуем получить и удалить пару элементов из нашей мапы.
Как получить и удалить элементы из HashMap
Чтобы удалить элемент, зная его ключ, используют метод remove:
numbersAndNames.put(1234567, "Андрей");
numbersAndNames.put(1333567, "Денис");
numbersAndNames.remove(1333567);
System.out.println(numbersAndNames);
Результат:
{1234567=Андрей}
Чтобы удалить сразу все элементы, используют функцию clear. Применяйте с осторожностью:
numbersAndNames.put(1234567, "Андрей");
numbersAndNames.put(1333567, "Денис");
numbersAndNames.clear();
System.out.println(numbersAndNames);
Результат:
{}
А чтобы получить значение по ключу, нужно использовать метод get:
numbersAndNames.put(1234567, "Андрей");
numbersAndNames.put(1333567, "Денис");
String denis = numbersAndNames.get(1333567);
System.out.println(denis);
Результат:
Денис
В HashMap у элементов нет индексов, как у массивов. Поэтому обращение к элементам происходит через ключи и, в некоторых случаях, через значения.
Ещё мы можем получить сразу все ключи или значения из HashMap. Для этого используют две функции: keySet — для ключей и values — для значений.
numbersAndNames.put(1234567, "Андрей");
numbersAndNames.put(1333567, "Денис");
System.out.println(numbersAndNames.keySet());
Результат:
[1234567, 1333567]
Теперь то же самое, но со значениями:
numbersAndNames.put(1234567, "Андрей");
numbersAndNames.put(1333567, "Денис");
System.out.println(numbersAndNames.values());
Результат:
[Андрей, Денис]
Видим, что ключи и значения вернулись в виде массива. Теперь к ним уже можно обращаться по индексам.
А для того чтобы получить все пары «ключ —значение», существует метод entrySet:
numbersAndNames.put(1234567, "Андрей");
numbersAndNames.put(1333567, "Денис");
System.out.println(numbersAndNames.entrySet());
Результат:
[1234567=Андрей, 1333567=Денис]
Таким способом можно превратить словарь в массив, который использует индексы. Например, это полезно, когда нужно последовательно перебирать все значения в словаре и как-то изменять их. Скажем, можем взять и убрать у всех имён лишние пробелы или заменить буквы «е» на «ё».
Как узнать размер HashMap
Если мы хотим узнать, сколько всего элементов находится в HashMap, можно использовать метод size:
numbersAndNames.put(1234567, "Андрей");
numbersAndNames.put(1333567, "Денис");
System.out.println(numbersAndNames.size());
Результат:
2
Проверить, не пустой ли HashMap, можно с помощью метода isEmpty:
numbersAndNames.put(1234567, "Андрей");
numbersAndNames.put(1333567, "Денис");
System.out.println(numbersAndNames.isEmpty());
Результат:
false
Как менять размер HashMap: методы resize и transfer
Выше мы рассказывали, что HashMap по умолчанию выделяет место для 16 элементов. А когда их количество достигает предельного коэффициента загрузки (стандартный — 75%), язык удвоит размер и позволяет вместить ещё 16 элементов.
Но, оказывается, этим процессом можно управлять и самостоятельно. Для этого существуют два метода — resize и transfer. Первый позволяет изменить размер HashMap, а второй — переместить элементы из одного словаря в другой. Сейчас разберёмся подробнее.
Внутрь метода resize передаётся новый размер в виде целого числа:
numbersAndNames.resize(40)
После выполнения этой команды, максимальный размер словаря станет 40 элементов. Как только их количество достигнет предела, он удвоится.
Метод transfer нужен, чтобы перенести все элементы из одного словаря в другой — обычно большего размера. При этом происходит перераспределение этих элементов: если в старом HashMap у элементов были длинные хвосты из данных, то в новом их быть не должно.
Например, давайте создадим новый словарь на 100 элементов, а затем переместим данные в него из другого.
HashMap<Integer, String> resizedNumbersAndNames = new resizedNumbersAndNames<>(100);
numbersAndNames.transfer(resizedNumbersAndNames);
System.out.println(resizedNumbersAndNames);
{1234567=Андрей, 1266667=Катя, 1333567=Денис}
Готово! Элементы переместились в новый словарь большего размера.
Шпаргалка: как работать с HashMap
Давайте разберём основные методы для работы с хеш-таблицами.
Метод | Что делает |
---|---|
import java.util.HashMap; | Импортирует библиотеку для работы со словарями |
HashMap<Type1, Type2> myHashMap = new HashMap<>(); | Создаёт новый словарь, где Type1 и Type2 — это типы данных ключей и значений |
myHashMap.put(key, value); | Добавляет элемент в словарь с ключом key и значением value |
myHashMap.containsKey(key) | Проверяет, есть ли в словаре элемент с ключом key |
myHashMap.remove(key); | Удаляет элемент с ключом key |
myHashMap.clear(); | Полностью очищает словарь |
myHashMap.get(key); | Возвращает элемент с ключом key |
myHashMap.keySet() | Возвращает список всех ключей из словаря |
myHashMap.values() | Возвращает список всех значений из словаря |
myHashMap.entrySet() | Возвращает список пар «ключ — значение» |
myHashMap.size() | Возвращает размер словаря |
myHashMap.isEmpty() | Проверяет, пуст ли словарь |
myHashMap.resize(newSize) | Изменяет максимальный размер словаря на значение newSize |
myHashMap.transfer(resizedHashMap) | Перемещает и перераспределяет элементы в новый словарь resizedHashMap |
Что запомнить
Давайте кратко подытожим, что мы узнали о хеш-таблицах:
- HashMap, или хеш-таблица, — структура данных, которая хранит элементы в формате «ключ — значение». Ключ — это уникальный номер элемента, а значение — содержимое элемента.
- Хеш-таблицы намного эффективнее обычных массивов при получении данных. Поэтому их уместно использовать, когда к данным приходится часто обращаться.
- Для каждого ключа HashMap создаёт свой хеш. Это такой числовой идентификатор, которые указывает элементу, в какую ячейку памяти записаться. Чтобы вычислять хеши, используют хеш-функции.
- В хеш-таблицах могут возникнуть коллизии — это когда хеш у двух элементов совпадает. В таком случае новый элемент добавляется в хвост старого, в результате в одной ячейке появляется целая цепочка данных. А заведует этим процессом система антиколлизий.
- Чтобы управлять элементами в HashMap используют разные методы — например, put, get, remove и size.
Больше интересного про код — в нашем телеграм-канале. Подписывайтесь!