Привіт! Сьогодні ми поговоримо про ще одну структуру даних – 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<integer, mem-invalid-attributes-holder=string>
entry: passportsAndNames.entrySet()) { System.out.println(entry); }
Інтерфейс Map.Entry позначає саме пару "ключ-значення" всередині словника.
Метод entrySet() повертає список усіх пар у нашій HashMap (оскільки наша мапа складається якраз із таких пар-Entry, то ми перебираємо саме пари, а не окремо ключі або значення).
Виведення: 212133=Лідія Аркадіївна Бублікова 8082771=Дональд Джон Трамп 162348=Іван Михайлович Серебряков
Не забудь вивчити офіційну документацію Oracle щодо HashMap.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ