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.
Комментарии (183)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
{Java_Shark} Уровень 12
26 февраля 2024
Отличная статья!!! Автору респект и +100 к карме)))
JavaRusher853 Уровень 18
16 февраля 2024
Очень интересная статья, реально не мог оторваться. Много новых (и повторение старых в том числе) и полезных методов, однозначно статья в копилку.
Denis Konshin Уровень 11
9 февраля 2024
интересненько
Алексей Уровень 33 Expert
30 декабря 2023
Хорошая статья 👍
Вячеслав Уровень 22 Expert
10 декабря 2023
🧐
Максим Li Уровень 36
5 декабря 2023
Отлично!
Alex Уровень 16
21 ноября 2023
Отличная статья, автору спасибо
Anatoly Уровень 29
16 августа 2023
okr
Alexander Rozenberg Уровень 32
18 июля 2023
fine
Skotique Уровень 35
27 июня 2023
довольно базовое описание контейнера хэшмэп. на деле это массив, из ячеек которого могут вырастать однонаправленные связные списки. сия конструкция может напоминать расческу с выломанными зубьями разной длины. эта лекция ни о чем, изучайте плотнее!