1. Множина пар: ключ–значення

>

У Java є ще одна цікава колекція (в широкому сенсі) — це колекція Map. Точного перекладу її назви немає: найчастіше її називають «карта», «словник» або просто «мапа» — саме цей термін ми й використовуватимемо надалі.

Ця колекція схожа на колекцію Set, але зберігає не множину елементів, а множину «пар елементів». Кожна пара елементів Map — це «ключ» і «значення».

Припустімо, ви хочете зберігати в програмі імена працівників компанії та їхні зарплати або імена своїх колег та їхній вік. Тоді вам знадобиться така таблиця:

Ім'я Вік
Сергій 21
Микола 22
Іван Петрович 48
Ганнуся ?

Тут у кожному рядку зберігається пара значень. Ім'я будемо називати ключем пари, а вік — значенням пари.

Увесь набір таких пар і буде називатися мапоюMap.

Ключем пари може бути що завгодно, але у деяких типів мап ключ не може мати значення null. Ключі мають бути унікальними: одна мапа не може містити два однакових ключі.


2. Клас HashMap

Клас HashMap — це найпопулярніша колекція з усіх мап (Map). З одного боку, він дуже схожий на HashSet і має всі його методи, а з іншого — на список (ArrayList), якби індексами списку могли бути не числа, а слова.

Створити об'єкт типу HashMap можна за допомогою такої команди:

HashMap<TКлюч, TЗначення> ім'я = new HashMap<TКлюч, TЗначення>();

де ТКлюч — це тип ключів у парі елементів, ТЗначення — тип значень у парі елементів, які зберігатимуться в колекції HashMap.

Клас HashMap має такі методи:

Метод Опис
void put(ТКлюч key, ТЗначення value)
Додає в колекцію пару (key, value)
ТЗначення get(ТКлюч key)
Повертає значення відповідно до ключа.
boolean containsKey(ТКлюч key)
Перевіряє наявність ключа в колекції
boolean containsValue(ТЗначення value)
Перевіряє наявність значення в колекції
ТЗначення remove(ТКлюч key)
Видаляє елемент із колекції
void clear()
Очищає колекцію: видаляє всі елементи
int size()
Повертає кількість пар елементів у колекції
Set<ТКлюч> keySet()
Повертає множину ключів колекції
Collection<ТЗначення> values()
Повертає множину елементів колекції
Set<Map.Entry<TКлюч, TЗначення>> entrySet()
Повертає всі значення колекції у вигляді множини (Set), яка містить пари (Map.Entry).

Додавання елементів до HashMap

Елементи додаються в мапу відразу парами: для цього використовується метод put(). Першим параметром передається ключ, а другим — значення.

HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("Сергій", 21);
map.put("Микола", 22);
map.put("Іван Петрович", 48);
map.put("Ганнуся", null);

Якщо під час додавання елемента з'ясується, що елемент із таким ключем уже є, старе значення ключа буде замінено новим.

Така поведінка робить HashMap схожим на масив або список, якби індексами в них були слова (String), а не числа.

Важливо!

Як Тип-Ключ і Тип-Значення можна використовувати практично будь-який тип. До Типу-Ключа є невеликі додаткові вимоги, але про них ви дізнаєтеся під час детального вивчення колекцій у квесті Java Collections.



3. Підмножини HashMap: множина ключів

Припустімо, ми хочемо просто вивести всі елементи HashMap на екран, як це зробити? Для цього потрібно зрозуміти, як перебрати всі елементи HashMap. Для цього є різні способи.

Найпростіший спосіб — використати цикл за ключами

Елементи класу HashMap не мають порядкових номерів, тому цикл із лічильником тут не підходить. Натомість можна отримати множину ключів за допомогою методу keySet(), а як перебирати множини, ви вже знаєте:

Код Опис
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("Сергій", 21);
map.put("Микола", 22);
map.put("Іван Петрович", 48);
map.put("Ганнуся", null);

for (String key: map.keySet())
{
   Integer value = map.get(key);
   System.out.println(key + " --> " + value);
}






Цикл за всіма ключами map

Отримуємо значення за ключем

Метод keySet() повертає множину ключів. Цю множину можна використати двома способами:

Короткий запис Довгий запис
for (String key: map.keySet())
{
   Integer value = map.get(key);
   System.out.println(key + " --> " + value);
}
Set<String> keys = map.keySet();

for (String key: keys)
{
   Integer value = map.get(key);
   System.out.println(key + " --> " + value);
}


4. Використання циклу за парами

Є складніший спосіб: можна перетворити Map на множину пар елементів, а потім використати цикл за елементами множини, як ми вже робили раніше.

Колекція HashMap має допоміжний клас для зберігання пари елементів. Він має приблизно такий вигляд:

class Entry<KeyType, ValueType>
{
   private KeyType key;
   private ValueType value;

   public KeyType getKey()
   {
      return this.key;
   }

   public ValueType getValue()
   {
      return this.value;
   }
}

Результат виклику методу entrySet() для об'єкта типу HashMap<ТКлюч, ТЗначення> матиме тип Set<Entry<ТКлюч, ТЗначення>>:

Set<Entry<Ключ, Значення>> ім'я = map.entrySet();

Тут ми бачимо складний тип Set із параметром-значенням, а параметр-значення — це ще один складний тип (Entry), який до того ж має два параметри.

Новачки можуть у цьому заплутатися. Проте якщо розберетеся, то зможете написати такий код:

HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("Сергій", 21);
map.put("Микола", 22);
map.put("Іван Петрович", 48);
map.put("Ганнуся", null);

Set<Map.Entry<String, Integer>> entries = map.entrySet();
for(Map.Entry<String, Integer> pair: entries)
{
   String key = pair.getKey();
   Integer value = pair.getValue();
   System.out.println(key + " --> " + value);
}

Цей код можна трошки спростити.

По-перше, можна не створювати окрему змінну для entries, а відразу викликати метод entrySet() всередині циклу for:

for(Map.Entry<String, Integer> pair: map.entrySet())
{
   String key = pair.getKey();
   Integer value = pair.getValue();
   System.out.println(key + " --> " + value);
}

По-друге, можна скористатися новим оператором var, який дає змогу автоматично вивести тип пари елементів:

for(var pair: map.entrySet())
{
   String key = pair.getKey();
   Integer value = pair.getValue();
   System.out.println(key + " --> " + value);
}

Непогано, чи не так?



5. Порівняння ArrayList і HashMap

HashMap дуже нагадує ArrayList, в якому індексами є не цифри, а слова (або інший тип ключів).

А якщо використовувати в HashMap ключ типу Integer, ця колекція стане ще більше схожою на ArrayList. Порівняйте:

Код з ArrayList<String> Код із HashMap<Integer, String>
ArrayList<String> list = new ArrayList<String>();

list.add("Привіт");
list.add("Hello");

String s = list.get(0);
list.set(0, s + "!");

for (String item: list)
{
   System.out.println(item);
}
HashMap<Integer, String> map = new HashMap<Integer, String>();

map.put(0, "Привіт");
map.put(1, "Hello");

String s = map.get(0);
map.put(0, s + "!");

for (String item: map.values())
{
   System.out.println(item);
}