Когда нужно быстро находить значение по ключу — номер телефона по имени, цену по артикулу или описание по коду — на помощь приходит HashMap. Это одна из самых часто используемых структур данных, и kotlin hashmap реализована очень удобно. В этой статье я простым языком объясню, как создать HashMap, добавить и получить элементы, проверить наличие ключа и перебрать все пары.
Что такое HashMap
HashMap — это коллекция, которая хранит пары «ключ — значение». Каждый ключ уникален, а значение может повторяться. Благодаря специальному внутреннему устройству поиск по ключу происходит очень быстро, почти мгновенно, независимо от размера коллекции. Для повседневных задач это лучший выбор.
Представьте обычный бумажный словарь: слово — ключ, его перевод — значение. Вы не листаете весь словарь, чтобы найти перевод, а открываете нужную букву. HashMap работает похоже, но вместо букв использует хеши — числа, которые вычисляются по ключу и указывают, в какой «ячейке» искать значение.
Создание HashMap
Самый простой способ — воспользоваться функцией mutableMapOf(). Она создаёт изменяемую HashMap и сразу позволяет добавить начальные пары через to. Если же нужна только для чтения, подойдёт mapOf().
val capitals = mutableMapOf(
"Россия" to "Москва",
"Франция" to "Париж",
"Япония" to "Токио"
)
val readOnly: Map<String, String> = mapOf(
"red" to "#ff0000",
"green" to "#00ff00"
)
Если хотите создать пустую HashMap и наполнять её позже, можно указать типы ключа и значения явно:
val scores = mutableMapOf<String, Int>()
// или через конструктор
val empty = HashMap<String, Int>()
Добавление и изменение элементов
В изменяемой HashMap можно добавлять новые пары или обновлять существующие. Kotlin предлагает два равноценных способа: метод put() и краткий синтаксис квадратных скобок.
val map = mutableMapOf<String, Int>()
map.put("apple", 5)
map["banana"] = 3
map["apple"] = 10 // обновление
println(map) // {apple=10, banana=3}
Разницы между put и [] нет, выбирайте тот, который лучше читается в вашем коде.
Получение значения по ключу
Достать значение можно несколькими способами, и каждый полезен в своей ситуации.
- [] — возвращает значение, если ключ найден, иначе
null. Безопасный вызов?.защищает от NullPointerException. - getValue() — выбрасывает исключение
NoSuchElementException, если ключа нет. Используйте, когда ключ обязан быть. - getOrElse() — даёт значение по ключу, а если его нет — выполняет лямбду и возвращает её результат.
- getOrDefault() — возвращает значение, а если ключ отсутствует — стандартное значение, которое вы указали.
val map = mapOf("a" to 1, "b" to 2)
println(map["a"]) // 1
println(map["c"]) // null
println(map.getValue("a")) // 1
println(map.getOrElse("c") { 0 }) // 0
println(map.getOrDefault("c", -1)) // -1
Проверка наличия ключа или значения
Часто нужно просто узнать, есть ли определённый ключ в HashMap или встречается ли какое-то значение. Для этого существуют два метода.
val map = mapOf("x" to 10, "y" to 20)
println("x" in map) // true, проверка ключа
println(map.containsKey("z")) // false
println(map.containsValue(20)) // true
Оператор in — краткая форма вызова containsKey. Для проверки значения используйте containsValue.
Удаление элементов
Из изменяемой HashMap можно удалять отдельные ключи или очистить её полностью.
val map = mutableMapOf("a" to 1, "b" to 2, "c" to 3)
map.remove("b")
println(map) // {a=1, c=3}
map.clear()
println(map) // {}
Метод remove возвращает удалённое значение или null, если ключ отсутствовал.
Перебор всех пар
Чтобы обработать все элементы HashMap, удобно использовать цикл for с деструктуризацией или метод forEach.
val fruits = mapOf("apple" to 5, "banana" to 3, "cherry" to 7)
// через for с деструктуризацией
for ((name, count) in fruits) {
println("$name: $count шт.")
}
// через forEach
fruits.forEach { (name, count) ->
println("$name = $count")
}
Обратите внимание, что порядок обхода HashMap не гарантирован. Если важен порядок добавления, используйте LinkedHashMap.
Изменяемые и неизменяемые Map
Новички часто путают mapOf и mutableMapOf. Первая возвращает неизменяемый объект, в который нельзя добавить элементы после создания. Вторая — изменяемый, с методами put, remove, clear.
val immutable = mapOf("a" to 1)
// immutable["b"] = 2 // ошибка компиляции
val mutable = mutableMapOf("a" to 1)
mutable["b"] = 2 // работает
Старайтесь использовать неизменяемые коллекции по умолчанию — это защищает от случайных изменений и делает код предсказуемым.
Пара слов о null и производительности
HashMap в Kotlin разрешает один null-ключ и сколько угодно null-значений. Но лучше избегать null-ключей без крайней необходимости.
val map = mutableMapOf<String?, Int?>()
map[null] = null
println(map) // {null=null}
По умолчанию начальная вместимость HashMap — 16 ячеек, а коэффициент заполнения — 0.75. Это значит, что когда количество элементов достигнет 12, массив автоматически расширится. Для большинства приложений менять эти параметры не нужно.
Практический пример: подсчёт частоты слов
Давайте применим полученные знания. Напишем функцию, которая принимает список слов и возвращает HashMap, где ключ — слово, а значение — сколько раз оно встретилось.
fun countWords(words: List<String>): Map<String, Int> {
val counter = mutableMapOf<String, Int>()
for (word in words) {
counter[word] = counter.getOrDefault(word, 0) + 1
}
return counter
}
val result = countWords(listOf("яблоко", "банан", "яблоко", "апельсин", "банан", "яблоко"))
println(result) // {яблоко=3, банан=2, апельсин=1}
Код читается просто: для каждого слова либо берём текущий счётчик и увеличиваем на 1, либо начинаем с нуля. HashMap идеально подходит для такой задачи.
Коротко о главном
Kotlin HashMap — это быстрый и удобный способ хранить данные в формате «ключ-значение». Вы научились создавать коллекцию через mutableMapOf(), добавлять элементы через [] и put, получать значения разными методами, проверять ключи и значения, удалять и перебирать пары. Освойте эти базовые операции — и работа с данными станет проще в любом проекте, от небольшого скрипта до серверного приложения. Для тренировки создайте HashMap с любимыми фильмами и рейтингами, добавьте пару записей и выведите их в цикле — это займёт пять минут, но даст полное понимание.