Привет! Сегодня мы поговорим о еще одной структуре данных — Map. Ее официальное русское название — “ассоциативный массив”, но его используют нечасто. Более распространены варианты “словарь”, “карта”, или (чаще всего) — сленговый англицизм “мапа” :)
Внутри Map данные хранятся в формате “ключ”-”значение”, то есть по парам. И в качестве ключей, и в качестве значений могут выступать любые объекты — числа, строки или объекты других классов.
Отличие Map от других структур данных
Ранее мы разбирали структуры данных, где элементы хранятся сами по себе. В массиве, или списке ArrayList/LinkedList мы храним какое-то количество элементов. Но что, если наша задача немного изменится? Например, представь себе, что перед нами стоит задача: создать список из 100 человек, где будет храниться ФИО человека и номер его паспорта. В принципе, это не так сложно. Например, можно уместить и то, и другое в строку, и создать список вот таких строк: “Анна Ивановна Решетникова, 4211 717171”. Но у такого решения сразу два недостатка. Во-первых, нам может понадобиться функция поиска по паспорту. А при таком формате хранения информации это будет проблематично. А во-вторых, ничто не помешает нам создать двух разных людей с одинаковыми номерами паспорта. И это самый серьезный недостаток нашего решения. Такие ситуации должны быть полностью исключены, не бывает двух людей с одинаковым номером паспорта. Тут на помощь нам приходит Map и ее заявленные особенности (хранение данных по паре в формате “ключ”-”значение”). Давай рассмотрим самую распространенную реализацию Map — Java класс HashMap.Создание 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.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ