User Professor Hans Noodles
Professor Hans Noodles
41 уровень

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.
Комментарии (98)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ СДЕЛАТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Art Rich Уровень 8, Russian Federation
8 октября 2021
Спасибо, всё понятно
Игорь Евгеньевич Уровень 28, Хабаровск, Россия
16 августа 2021
Спасибо за уточнение что мне статью читать пока рановато, а то я в процессе чтения чуть не закомплексовал. Сохранил в закладки, в будущем когда статья ох как пригодиться отпишусь.
Nadezhda Goncharova Уровень 27
29 июня 2021
Хорошая статья, спасибо :)
Yarik Уровень 33, Оренбург, Россия
2 мая 2021
Каждый новый автор ВСЕГДА добавляет какой то ранее неизвестной информации....
Maks Panteleev Уровень 41, Москва, Россия
22 марта 2021
Чем мне нравится джавараш - сначала надают миллиард задач на использование определенных знаний, методов и тд, а потом когда ты уже все решил и все нагугли, дают тебе лекции, где рассказывают об этих методах и дают эти знания)
kajuga Уровень 34, Казань, Россия
12 января 2021
"можно по-отдельности получить список всех ключей и всех значений" нет, не совсем так, а: множество (Set) всех ключей и список (List) всех значений - ровно об этом и указано в примере.
DAS Уровень 8, Москва, Россия
10 ноября 2020
Хорошая статья, очень доступное объяснение. Отложил в закладки.
🦔 Виктор Уровень 20, Москва, Россия Expert
20 октября 2020
Спасибо за отличную статью, которая по полочкам раскладывает HashMap. Её бы внедрить в курс как можно раньше, до соответствующих задач.
Артур Уровень 10, Гродно, Беларусь
15 октября 2020
Не понимаю, почему я это читаю после предыдущей порции задач на хэшмэпы и операции с ключами-значениями? Я думаю, лучше бы усвоилось, если бы я не копипастил код из предыдущих невнятных лекций, а осознанно применял знания из этой статьи.
Sergey Уровень 1, Алматы
7 октября 2020
реально очень доходчиво