Привет! Сегодня мы поговорим о еще одной структуре данных, Map. Ее официальное русское название это "ассоциативный массив", но его используют нечасто. Более распространены варианты "словарь", "карта", или (чаще всего) сленговый англицизм "мапа" :)
Коротко:
HashMap это структура данных, которая хранит информацию парами ключ-значение. Ключи всегда уникальны, а доступ к значению идет по ключу. Добавить пару можно методом
put(), получить значение через
get(), удалить через
remove(), а перебрать все пары через
entrySet().
Внутри Map данные хранятся в формате "ключ"-"значение", то есть по парам. И в качестве ключей, и в качестве значений могут выступать любые объекты: числа, строки или объекты других классов.
Кратко
- Map (мапа, словарь) хранит данные парами ключ-значение; самая частая реализация это
HashMap.
- Ключ в
HashMap всегда уникален; повторная запись по тому же ключу перезаписывает значение.
- Доступ к значению только по ключу (номерных индексов, как в списках, здесь нет).
- Добавить пару:
put(); получить: get(); удалить: remove().
- Проверка:
containsKey(), containsValue(), isEmpty(); размер: size(); очистка: clear().
- Все ключи:
keySet(); все значения: values(); все пары: entrySet(); объединить мапы: putAll().
Отличие Map от других структур данных
Ранее мы разбирали структуры данных, где элементы хранятся сами по себе. В массиве, или списке
ArrayList/
LinkedList мы храним какое-то количество элементов (подробнее в статье про
работу ArrayList).
Но что, если наша задача немного изменится?
Например, представь себе, что перед нами стоит задача: создать список из 100 человек, где будет храниться ФИО человека и номер его паспорта.
В принципе, это не так сложно. Например, можно уместить и то, и другое в строку, и создать список вот таких строк:
"Анна Ивановна Решетникова, 4211 717171".
Но у такого решения сразу два недостатка:
- Нам может понадобиться функция поиска по паспорту, а при таком формате хранения информации это будет проблематично.
- Ничто не помешает нам создать двух разных людей с одинаковыми номерами паспорта. Это самый серьезный недостаток: не бывает двух людей с одинаковым номером паспорта, и такие ситуации должны быть полностью исключены.
Тут на помощь нам приходит Map и ее заявленные особенности (хранение данных по паре в формате "ключ"-"значение"). Давай рассмотрим самую распространенную реализацию Map,
Java класс HashMap.
![HashMap в Java: хранение данных парами ключ-значение]()
Создание 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<Integer, String> entry: passportsAndNames.entrySet()) {
System.out.println(entry);
}
Интерфейс
Map.Entry обозначает как раз пару "ключ-значение" внутри словаря.
Метод
entrySet() возвращает список всех пар в нашей HashMap (поскольку наша мапа состоит как раз из таких пар-Entry, то мы перебираем именно пары, а не отдельно ключи или значения).
Вывод:
212133=Лидия Аркадьевна Бубликова
8082771=Дональд Джон Трамп
162348=Иван Михайлович Серебряков
Основные методы HashMap
Чтобы все методы были перед глазами, соберем их в таблицу:
| Метод | Что делает |
put(ключ, значение) | добавляет пару или перезаписывает значение по существующему ключу |
get(ключ) | возвращает значение по ключу |
remove(ключ) | удаляет пару по ключу |
containsKey(ключ) | есть ли такой ключ (true или false) |
containsValue(значение) | есть ли такое значение (true или false) |
keySet() | возвращает все ключи (в виде Set) |
values() | возвращает все значения |
entrySet() | возвращает все пары ключ-значение (для перебора в цикле) |
size() | число пар в мапе |
clear() | удаляет все пары |
isEmpty() | пустая ли мапа (true или false) |
putAll(другаяМапа) | добавляет все пары из другой мапы |
Полный список методов есть в
документации Oracle (Class HashMap).
А вот эту статью сохрани себе на будущее:
как HashMap устроена изнутри. Сейчас ее пока читать рановато, но в будущем, когда ты набьешь руку в использовании HashMap, она поможет тебе разобраться как эта структура данных устроена внутри.
Читайте также
Вопросы и ответы
Что такое HashMap в Java?
Это реализация Map (словаря), которая хранит данные парами ключ-значение. Ключи уникальны, а доступ к значению осуществляется по ключу.
Чем HashMap отличается от ArrayList?
В
ArrayList элементы хранятся по числовым индексам, а в
HashMap по уникальным ключам. HashMap хранит пары ключ-значение, а список только значения.
Могут ли в HashMap повторяться ключи?
Нет, ключ всегда уникален. Если добавить пару с уже существующим ключом, старое значение перезапишется новым.
Как получить значение из HashMap?
Через метод
get(), передав ему ключ. Получить ключ по значению нельзя, потому что значения могут повторяться.
Как перебрать все пары HashMap в цикле?
Через
entrySet(): он возвращает все пары ключ-значение (
Map.Entry), которые можно перебрать в цикле for.
Как объединить две HashMap?
Методом
putAll(): вызвать его у первой мапы и передать вторую, тогда все пары из второй добавятся в первую.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
нет