Карты в Kotlin (HashMap) — Руководство для начинающих

Карты очень полезны если нужно быстро найти значения с помощью определенного идентификатора. Например, в оглавлении книги названия глав сопоставляются с номерами страниц, поэтому по книге становится легче ориентироваться.

Чем карта отличается от массива? Из массива можно получить значение только по его индексу, который должен быть целым числом, и все индексы должны быть последовательными. В карте — ключи могут быть любого типа и, как правило, не расположены в определенном порядке.

Содержание статьи

Карта представляется собой неупорядоченную коллекцию пар. Каждая пара состоит из ключа и её значения.

карты в kotlin

Как показано на схеме выше, сами ключи уникальны. Один и тот же ключ не может появляться в карте дважды, но разные ключи могут иметь одно и то же значение. Все ключи должны быть одного типа, и все значения также должны быть одного типа.

Создание карты в Kotlin

Самым простым способом создания карты является использование функции mapOf() из стандартной библиотеки Kotlin. Данная функция принимает список объектов Pair, которые разделены запятыми:

Объекты Pair создаются через использование инфиксной функции to. Обратите внимание, что Map<K, V> является интерфейсом, о котором мы подробнее поговорим позже. Конкретный создаваемый тип зависит от того, какая функция стандартной библиотеки вызывается. Функция mapOf() возвращает неизменяемую карту фиксированного размера.

Представьте, что вы делаете карточную игру. Вместо использования двух массивов для сопоставления игроков с их баллами, как мы это делали в одном из прошлых уроков, можно использовать карту:

В данном примере тип карты выводится как MutableMap<String, Int>. Это значит, что переменная namesAndScores является картой, в которой ключи являются строками, а её значения — целые числа. То есть, это карта из строк и целых чисел.

При выводе содержимого карты можно заметить, что пары не упорядочены. Помните, что в отличие от массивов, порядок данных в карте не гарантирован.

Для создания пустой карты не нужно передавать никакие аргументы функциям стандартной библиотеки, просто пишем так:

Также, пустую карту можно создать, вызвав конструктор карты:

Здесь не требуется уточнение типа переменной, так как компилятор может определить тип по вызываемому конструктору.

При создании карты можно определить её вместимость:

Это простой способ улучшить производительность, когда неизвестно, сколько данных должна хранить карта.

Получение доступа к значениям карты в Kotlin

Как и в случае с массивами, есть несколько способов получить доступ к значениям  из карты.

Получение значения карты по ключу

Карты поддерживают использование квадратных скобок для получения доступа к их значениям. В отличие от массивов, вам не нужно указать индекс, карта имеет ключи по которым можно получить их значение.

К примеру, если вам нужен счет игрока Anna, то можно использовать следующий код:

Карта проверит, есть ли в наличии пара с ключом Anna, и если да, то будет возвращено её значение. Если карта не находит ключ, тогда результатом будет null.

В случае массивов если вы указали несуществующий индекс, тогда появится ошибка, но с картами такого не происходит.

Доступ к значениям в картах по ключу очень эффективен. Так можно узнать, участвует ли в игре определенный игрок, без необходимости перебирать все ключи, как в случае с массивами.

Свойства и методы карт в Kotlin

В дополнение к использованию индексации по ключу для получения доступа к значению карты можно попробовать метод get():

Использование метода get() не рекомендуется, лучше используйте квадратные скобки и укажите в них нужный вам ключ.

Карты имеют много общих свойств и методов с другими типами коллекций. К примеру, у массивов как и у карт есть метод isEmpty() который проверяет если карта пуста или нет. Параметр size который указывает на кол-во пар в карте.

Манипуляция картами в Kotlin

Создать карту и получить доступ к ее содержимому достаточно просто — но как обстоят дела с его изменением? Для это нам потребуется изменяемая карта (mutableMap).

Добавление новых данных в карту

Bob решил присоединиться к игре.

карты в kotlin

Перед тем, как дать ему разрешение, давайте взглянем на карту с его данными:

Тип данной карты является MutableMap<String, String>. Представьте, что о пользователе Bob были получены новые данные и вы решили добавить их в карту. Это можно сделать следующим образом:

Есть и более короткий способ добавления пары в карту, используя уже знакомые нам квадратные скобки:

Bob является профессиональным карточным игроком. Он станет хорошим дополнением в вашем списке.

Задание для проверки

Напишите функцию, которая выводит на экран город и штат указанного игрока.

Изменение значений в карте

Внезапно выяснилось, что в прошлом игрок Bob жульничал при игре в карты. Он не просто профессионал — он карточная акула. Bob попросил вас изменить его имя и профессию, чтобы его никто не узнал.

Так как Bob действительно хочет избавиться от своего темного прошлого, вы решили ему помочь. Сначала вы изменили его имя с Bob на Bobby.

Вы видели метод put() выше, когда читали про добавления новых пар в карту. Почему он возвращает строку Bob?

Метод put(key: K, value: V): V? заменяет значение указанного ключа новым значением и возвращает старое значение. Если такого ключа не существует, этот метод добавит новую пару и вернет null.

Вы можете изменить значение ключа при использовании тех же квадратных скобок, указав ключ и новое значение для него.

Результат:

Данный метод обновит значение ключа или добавит новую пару в карту если её не существует.

Для добавления новой пары в карту — можно воспользоваться оператором +=:

Результат:

Удаление данных из карты

Игрок Bob, то есть Bobby, все еще не чувствует себя в безопасности. Он хочет, чтобы вы удалили всю информацию о его месте жительства.

Результат:

Первый вызов метода remove() удалит ключ city и его значение из карты. Второй вызов удалит ключ только в том случае, если его значение совпадает со вторым аргументом.

Итерация по карте в Kotlin

Цикл for-in применяется когда нам нужно итерировать данные из карты. Давайте взглянем на список игроков и их счет в игре:

Результат:

Также, можно итерировать только ключи из самой карты. Для этого используйте свойство keys:

Результат:

Можно итерировать только по значениям из карты с помощью её свойства values.

Результат:

Производительность кода при работе с картами

Чтобы изучить, как работают карты, нужно понимать, что такое хеширование. Хеширование представляет собой процесс преобразования значения из String, Int, Double, Boolean и так далее — в числовое значение, известное как хеш-значение. Затем это значение можно использовать для быстрого поиска значений в хеш-таблице.

Тип Any имеет метод hashCode(), который возвращает хеш-значение для любого объекта. У всех базовых типов есть хеш-значение. К примеру:

Хеш-значение должно быть детерминированным — это означает, что данное значение должно всегда возвращать одинаковое хеш-значение. Неважно, сколько раз генерируется хеш-значение для строки some string, оно всегда будет выдаваться одинаковое значение 1395333309.

Никогда не сохраняйте хеш-значение, поскольку нет гарантии, что оно будет одинаковым при запуске программы. Ниже обсуждается производительность различных хеш-операций с картой. Производительность зависит от наличия хорошей функции хеширования, которая избегает повтора результата хеширования для разных значений. Если у вас плохая функция хеширования, все операции ниже дегенерируются до линейного времени, или производительности O(n). К счастью, у встроенных типов есть отличные имплементации метода hashCode() для общего назначения.

Получение доступа к элементам: Получение значения для ключа является операцией с постоянным временем, или О(1);

Вставка элементов: При вставки элемента, карта должна посчитать хеш-значение ключа и затем сохранить данные на основании данного хеша. Это О(1) операция;

Удаление элементов: Карта должна опять посчитать хеш-значение, чтобы точно знать, где искать элемент для удаления. Это также О(1) операция;

Поиск элемента: Как упоминалось выше, у процесса получения доступа к элементу постоянное время выполнения, поэтому сложность для поиска также является О(1);

Если вам важна производительность кода, то для создания карты HashMap<K, V> следует использовать функцию hashMapOf() вместо mapOf().

Ключевые особенности карт в Kotlin

  • Карта — это неупорядоченная коллекция пар ключей-значений;
  • Все ключи карты одного типа, все значения также одного типа;
  • Используйте индексацию для получения и добавления значений, обновления или удаления пар;
  • Если запрашиваемого ключа нет в карте, то возвращается значение null;
  • Встроенные типы данных, такие как String, Int, Double, изначально имеют более эффективные хэш-значения;
  • Инициализируйте карту через HashMap<K, V> если вам важна производительность кода.

Задачи по картам в Kotlin

Выполните следующие задания, чтобы проверить, насколько хорошо вы усвоили материал по картам.

1.1 Какие из следующих строчек кода правильно инициализируют карту?

1.2 Дается следующая карта для изучения:

Какие из нижепредставленных строк будут правильно работать?

1.3 Дается следующая карта для изучения:

Какая из следующих строчек кода неправильная?

2. Напишите функция которая выведет на экран значение из карты где оно длиннее 8 символов.

Имея текущую карту, наша функция должна вывести на экран строку «California» потому, что только у нее более 8 символов.

Объединение двух карт в одну

3. Напишите функцию, которая объединяет две карты в одну. Если определенный ключ появляется в обеих картах, игнорируйте пару из первой карты. Это сигнатура функции выглядит следующим образом:

Посчитать сколько раз символ встречается в строке

4. Создайте функцию occurrencesOfCharacters, которая вычисляет, какие символы встречаются в строке и как часто. Игнорируйте пробелы в строке. Результат должен возвращаться как карта.

Подсказка: Тип String является коллекцией символом, по которой можно итерировать с помощью цикла for.

Бонус: У карт есть специальная функция, которая позволяет получить значение по умолчанию, если оно не найдено в карте. Например, функция map.getOrDefault('a', defaultValue = 0) вернет 0 вместо простого возврата null если символ a не был найден в карте.

Например, для строки «moon» функция вернет такую карту:

Проверка если все значения из карты уникальные

5. Напишите функцию, которая возвращает true, если все значения из карты являются уникальными. Используйте множество для проверки уникальности. Это сигнатура функции:

6. Дается карта:

Измените значение ключа "Patrick" на null и полностью удалите ключ "Ray" из карты.

0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x