JavaRush /Java блог /Java Developer /HashMap в Java— что за карта такая?
Автор
Milan Vucic
Репетитор по программированию в Codementor.io

HashMap в Java— что за карта такая?

Статья из группы Java Developer
Привет! Сегодня мы поговорим о еще одной структуре данных — Map. Ее официальное русское название — “ассоциативный массив”, но его используют нечасто. Более распространены варианты “словарь”, “карта”, или (чаще всего) — сленговый англицизм “мапа” :) Внутри Map данные хранятся в формате “ключ”-”значение”, то есть по парам. И в качестве ключей, и в качестве значений могут выступать любые объекты — числа, строки или объекты других классов.

Отличие Map от других структур данных

Ранее мы разбирали структуры данных, где элементы хранятся сами по себе. В массиве, или списке ArrayList/LinkedList мы храним какое-то количество элементов. Но что, если наша задача немного изменится? Например, представь себе, что перед нами стоит задача: создать список из 100 человек, где будет храниться ФИО человека и номер его паспорта. В принципе, это не так сложно. Например, можно уместить и то, и другое в строку, и создать список вот таких строк: “Анна Ивановна Решетникова, 4211 717171”. Но у такого решения сразу два недостатка. Во-первых, нам может понадобиться функция поиска по паспорту. А при таком формате хранения информации это будет проблематично. А во-вторых, ничто не помешает нам создать двух разных людей с одинаковыми номерами паспорта. И это самый серьезный недостаток нашего решения. Такие ситуации должны быть полностью исключены, не бывает двух людей с одинаковым номером паспорта. Тут на помощь нам приходит Map и ее заявленные особенности (хранение данных по паре в формате “ключ”-”значение”). Давай рассмотрим самую распространенную реализацию Map — Java класс HashMap.HashMap — что за карта такая? - 1

Создание HashMap в Java и работа с классом

Создается данная реализация очень просто:

public static void main(String[] args) { 

   HashMap<Integer, String> passportsAndNames = new HashMap<>(); 

} 
Здесь мы создали словарь, в котором элементы будут храниться в формате “число-строка”. Число будет выступать ключом, а строка — значением. Также мы указали какого типа у нас будут ключи (Integer), а какого — значения (String). Почему именно так? Во-первых, ключ в HashMap всегда является уникальным. Для нас это отлично подойдет, поскольку мы сможем использовать номер паспорта в качестве ключа и избежать повторов. А строка с ФИО будет выступать значением (ФИО у разных людей легко могут повторяться, в этом ничего страшного для нас нет).

Добавление новой пары в HashMap

Данная задача выглядит так:

public class Main { 

   public static void main(String[] args) { 
       HashMap<Integer, String> passportsAndNames = new HashMap<>(); 

 
       passportsAndNames.put(212133, "Лидия Аркадьевна Бубликова"); 
       passportsAndNames.put(162348, "Иван Михайлович Серебряков"); 
       passportsAndNames.put(8082771, "Дональд Джон Трамп"); 
       System.out.println(passportsAndNames); 

   } 

}
Для этого используется метод put(). Кроме того, HashMap имеет переопределенный метод toString(), поэтому ее можно выводить на консоль. Вывод будет выглядеть так: {212133=Лидия Аркадьевна Бубликова, 8082771=Дональд Джон Трамп, 162348=Иван Михайлович Серебряков}

Особенности ключей HashMap

Теперь давай проверим, действительно ли ключи являются уникальными? Попробуем добавить новый элемент с уже имеющимся в мапе ключом:

public static void main(String[] args) { 

   HashMap<Integer, String> passportsAndNames = new HashMap<>(); 

   passportsAndNames.put(212133, "Лидия Аркадьевна Бубликова"); 
   passportsAndNames.put(162348, "Иван Михайлович Серебряков"); 
   passportsAndNames.put(8082771, "Дональд Джон Трамп"); 
   passportsAndNames.put(162348, "Виктор Михайлович Стычкин");//повторный ключ 

   System.out.println(passportsAndNames); 

}
Вывод: {212133=Лидия Аркадьевна Бубликова, 8082771=Дональд Джон Трамп, 162348=Виктор Михайлович Стычкин} Предыдущий элемент с ключом 162348, как видишь, был перезаписан. “Ключ” назвали ключом не просто так. Доступ к значениям в HashMap осуществляется по ключу (но никак не наоборот — ключ нельзя получить по значению, ведь значения могут быть повторяющимися). Это хорошо видно на примерах получения элемента, а также удаления элемента из HashMap:

public static void main(String[] args) { 

   HashMap<Integer, String> passportsAndNames = new HashMap<>(); 

   passportsAndNames.put(212133, "Лидия Аркадьевна Бубликова"); 
   passportsAndNames.put(162348, "Иван Михайлович Серебряков"); 
   passportsAndNames.put(8082771, "Дональд Джон Трамп"); 

   String lidiaName = passportsAndNames.get(212133); 
   System.out.println(lidiaName); 


   passportsAndNames.remove(162348); 
   System.out.println(passportsAndNames); 

}
Для того, чтобы получить значение, или удалить пару из словаря, мы должны передать в методы get() и remove() именно уникальный ключ, соответствующий этому значению. Номерных индексов, как в массивах или списках, в HashMap нет — доступ к значению осуществляется по ключу. Вывод в консоль: Лидия Аркадьевна Бубликова {212133=Лидия Аркадьевна Бубликова, 8082771=Дональд Джон Трамп}

Проверка наличия ключа и значения

В классах ArrayList и LinkedList мы могли проверить, содержится ли в списке какой-то конкретный элемент. HashMap тоже позволяет это делать, причем для обеих частей пары: у нее есть методы containsKey()(проверяет наличие какого-то ключа) и containsValue() (проверяет наличие значения).

public static void main(String[] args) { 

   HashMap<Integer, String> passportsAndNames = new HashMap<>(); 

   passportsAndNames.put(212133, "Лидия Аркадьевна Бубликова"); 
   passportsAndNames.put(162348, "Иван Михайлович Серебряков"); 
   passportsAndNames.put(8082771, "Дональд Джон Трамп"); 


   System.out.println(passportsAndNames.containsKey(11111)); 
   System.out.println(passportsAndNames.containsValue("Дональд Джон Трамп")); 

}
Вывод: false true

Получение списка всех ключей и значений

Еще одна удобная особенность HashMap — можно по-отдельности получить список всех ключей и всех значений. Для этого используются методы keySet() и values():

public class Main { 
 
   public static void main(String[] args) { 

       HashMap<Integer, String> passportsAndNames = new HashMap<>(); 

       passportsAndNames.put(212133, "Лидия Аркадьевна Бубликова"); 
       passportsAndNames.put(162348, "Иван Михайлович Серебряков"); 
       passportsAndNames.put(8082771, "Дональд Джон Трамп"); 

       Set<Integer> keys = passportsAndNames.keySet(); 
       System.out.println("Ключи: " + keys); 

       ArrayList<String> values = new ArrayList<>(passportsAndNames.values()); 
       System.out.println("Значения: " + values); 

   } 

}
Ключи извлекаются в коллекцию Set. Ее особенность в том, что в ней не может быть повторяющихся элементов. Сейчас главное запомни, что список всех ключей можно вынести из HashMap в отдельную коллекцию. Значения мы в примере сохранили в обычный ArrayList. Вывод в консоль: Ключи: [212133, 8082771, 162348] Значения: [Лидия Аркадьевна Бубликова, Дональд Джон Трамп, Иван Михайлович Серебряков] Методы size() и clear() делают ровно то же самое, что и в предыдущих структурах, которые мы проходили: первый — возвращает число элементов в словаре на текущий момент, второй — удаляет все элементы.

public static void main(String[] args) { 

   HashMap<Integer, String> passportsAndNames = new HashMap<>(); 

   passportsAndNames.put(212133, "Лидия Аркадьевна Бубликова"); 
   passportsAndNames.put(162348, "Иван Михайлович Серебряков"); 
   passportsAndNames.put(8082771, "Дональд Джон Трамп"); 

   System.out.println(passportsAndNames.size()); 
   passportsAndNames.clear(); 
   System.out.println(passportsAndNames); 

}
Вывод: 3 {} Для проверки того, есть ли в нашей HashMap хотя бы один элемент, можно использовать метод isEmpty():

public static void main(String[] args) { 

   HashMap<Integer, String> passportsAndNames = new HashMap<>(); 

   passportsAndNames.put(212133, "Лидия Аркадьевна Бубликова"); 
   passportsAndNames.put(162348, "Иван Михайлович Серебряков"); 
   passportsAndNames.put(8082771, "Дональд Джон Трамп"); 
 
   if (!passportsAndNames.isEmpty()) { 

       System.out.println(passportsAndNames); 

   } 

}
Вывод: {212133=Лидия Аркадьевна Бубликова, 8082771=Дональд Джон Трамп, 162348=Иван Михайлович Серебряков} Теперь вывод на консоль у нас будет осуществляться только после предварительной проверки:)

Объединение двух мап в одну

Еще один интересный момент — две мапы можно объединить в одну. Для этого существует метод putAll(). Мы вызываем его у первой HashMap, передаем вторую в качестве аргумента, и элементы из второй будут добавлены в первую:

public static void main(String[] args) { 

   HashMap<Integer, String> passportsAndNames = new HashMap<>(); 
   HashMap<Integer, String> passportsAndNames2 = new HashMap<>(); 
 
   passportsAndNames.put(212133, "Лидия Аркадьевна Бубликова"); 
   passportsAndNames.put(162348, "Иван Михайлович Серебряков"); 
   passportsAndNames.put(8082771, "Дональд Джон Трамп"); 
 
   passportsAndNames2.put(917352, "Алексей Андреевич Ермаков"); 
   passportsAndNames2.put(925648, "Максим Олегович Архаров"); 


   passportsAndNames.putAll(passportsAndNames2); 
   System.out.println(passportsAndNames); 

}
Вывод: {917352=Алексей Андреевич Ермаков, 212133=Лидия Аркадьевна Бубликова, 8082771=Дональд Джон Трамп, 925648=Максим Олегович Архаров, 162348=Иван Михайлович Серебряков} Все элементы passportsAndNames2 были скопированы в passportsAndNames. Теперь рассмотрим пример посложнее. А именно — перебор HashMap в цикле.

for (Map.Entry entry: passportsAndNames.entrySet()) { 

   System.out.println(entry); 

}
Интерфейс Map.Entry обозначает как раз пару “ключ-значение” внутри словаря. Метод entrySet() возвращает список всех пар в нашей HashMap (поскольку наша мапа состоит как раз из таких пар-Entry, то мы перебираем именно пары, а не отдельно ключи или значения). Вывод: 212133=Лидия Аркадьевна Бубликова 8082771=Дональд Джон Трамп 162348=Иван Михайлович Серебряков Сохрани себе на будущее вот эту статью: https://habr.com/ru/post/128017/ Сейчас ее пока читать рановато, но в будущем, когда ты набьешь руку в использовании HashMap, она поможет тебе разобраться как эта структура данных устроена изнутри. Кроме того, не забудь изучить официальную документацию Oracle по HashMap.
Комментарии (176)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Alex Уровень 15
21 ноября 2023
Отличная статья, автору спасибо
Anatoly Уровень 22
16 августа 2023
okr
Alexander Rozenberg Уровень 30
18 июля 2023
fine
Skotique Уровень 35
27 июня 2023
довольно базовое описание контейнера хэшмэп. на деле это массив, из ячеек которого могут вырастать однонаправленные связные списки. сия конструкция может напоминать расческу с выломанными зубьями разной длины. эта лекция ни о чем, изучайте плотнее!
No Name Уровень 32
11 июня 2023
+ статья в копилку
Ислам Уровень 32
31 мая 2023
Nice
Sabir Ali Уровень 7
10 апреля 2023
“Анна Ивановна Решетникова, 4211 717171” И как они собирались хранить такой номер паспорта в типе Integer? Тем самым показав, что номер паспорта хранить в базе данных как число - это не лучшая идея. Так как номера паспорта могут содержать дефисы, пробелы, буквы и т.д. Поправьте меня, если я не прав. Иначе у меня вопросы к господину Милану.
Sm1le84 Уровень 10 Student
25 февраля 2023
Скажите, почему хронология вывода на экран поменялась?
ChupaFx Уровень 32
24 февраля 2023
Получение списка всех ключей и значений Еще одна удобная особенность HashMap — можно по-отдельности получить список всех ключей и всех значений. Для этого используются методы keySet() и values() Для чего в примере создаются Set и ArrayList? Если в sout можно прямо вызвать методы keySet() и values()?
Denis Odesskiy Уровень 29
12 февраля 2023
Возможно ли в HashMap переопределить метод toString() и если да, то каким образом? P/S: Спросил нынче модный ChatGPT, так он какую-то чушь городит: то пишет можно и даёт нерабочие коды, каждый раз разные, то пишет что нельзя потому что HashMap происходит от абстрактного класса. Хотя я уже понял что верить ему нельзя во всем, на вопрос можно ли реализовать в Java множественное наследование (не через интерфейсы) он бодро рапортовал, что конечно можно и вывалил код типа public сlass A extends B, C, который естественно не работает. Извините за возможный оффтоп... Так что роботы пока не завоюют мир, поэтому, вопрос по toString() все еще актуален...:)