Avatar ·

Как перебрать (итерировать) HashMap в Kotlin? Все способы с примерами

Привет! Я только начал изучать Kotlin после Java и столкнулся с простой, но важной задачей. У меня есть HashMap, в котором хранятся пары "ключ-значение", например, список пользователей и их ID. Мне нужно пройтись по всем элементам этой карты и выполнить с ними какие-то действия: вывести в консоль, отфильтровать, или преобразовать в другой список. В Java я привык делать это через entrySet() или keySet() в цикле for-each. Но в Kotlin, как я понимаю, есть много более современных и лаконичных способов. Я пробовал просто написать for (item in map), но компилятор ругается. Вот пример моего кода, который не работает: ```kotlin fun main() { val users: HashMap = hashMapOf( 1 to "Анна", 2 to "Борис", 3 to "Виктор" ) // Пытаюсь перебрать как обычный список - ошибка! for (user in users) { println(user) } } ``` Подскажите, пожалуйста, как правильно итерировать HashMap в Kotlin? Какие есть лучшие практики? Мне важны не только базовые способы, но и современные подходы с использованием функций высшего порядка, если они есть. Заранее спасибо! P.S. Интересуют также моменты: как получить только ключи, только значения, как изменить значения на лету, и как это сделать в одну строку без лишних циклов. <<>>

1. Проблема с простым циклом for

В вашем коде ошибка в том, что HashMap сам по себе не реализует интерфейс Iterable для пар "ключ-значение" напрямую. Компилятор ожидает, что вы укажете, как именно вы хотите итерировать: по ключам, по значениям или по парам.

Вот как это исправить самым простым способом — используя деструктуризацию в цикле for:

fun main() {
    val users: HashMap<Int, String> = hashMapOf(
        1 to "Анна",
        2 to "Борис",
        3 to "Виктор"
    )
    
    // ✅ Правильный способ: перебор по парам (ключ, значение)
    for ((id, name) in users) {
        println("Пользователь $name имеет ID = $id")
    }
}

2. Основные способы итерации

Kotlin предоставляет несколько удобных методов для работы с HashMap. Вот полный список:

  • По парам "ключ-значение": for ((k, v) in map) { ... } — самый частый вариант.
  • Только по ключам: for (key in map.keys) { ... }
  • Только по значениям: for (value in map.values) { ... }
  • Через entrySet(): for (entry in map.entries) { ... } — Java-стиль, но тоже работает.

3. Функциональный стиль (рекомендуемый)

В современном Kotlin принято использовать функции высшего порядка для работы с коллекциями. Это делает код более читаемым и лаконичным:

fun main() {
    val users = hashMapOf(1 to "Анна", 2 to "Борис", 3 to "Виктор")
    
    // forEach с деструктуризацией
    users.forEach { (id, name) -> 
        println("$id -> $name")
    }
    
    // Преобразование ключей
    val updatedKeys = users.mapKeys { it.key + 10 }
    println(updatedKeys) // {11=Анна, 12=Борис, 13=Виктор}
    
    // Преобразование значений (например, сделать все имена заглавными)
    val upperNames = users.mapValues { it.value.uppercase() }
    println(upperNames) // {1=АННА, 2=БОРИС, 3=ВИКТОР}
    
    // Фильтрация
    val filtered = users.filter { it.key > 1 }
    println(filtered) // {2=Борис, 3=Виктор}
    
    // Получить список значений, преобразованных в другой тип
    val nameLengths = users.map { (id, name) -> "ID=$id, Длина имени=${name.length}" }
    println(nameLengths) // [ID=1, Длина имени=4, ...]
}

4. Важные нюансы

  • Изменение во время итерации: Нельзя добавлять или удалять элементы из HashMap во время итерации через forEach или for. Это вызовет ConcurrentModificationException. Если нужно изменить, используйте mutableMap и итератор:
val mutableUsers = users.toMutableMap()
val iterator = mutableUsers.iterator()
while (iterator.hasNext()) {
    val (id, name) = iterator.next()
    if (id == 2) iterator.remove() // безопасное удаление
}
  • Производительность: Все перечисленные методы (кроме явного iterator()) имеют сложность O(n), так как проходят по всей карте. Для больших карт (миллионы записей) используйте asSequence() для ленивых вычислений:
users.asSequence()
    .filter { it.key % 2 == 0 }
    .map { it.value.uppercase() }
    .toList() // только здесь выполняется

5. Итоговая рекомендация

Для 90% случаев используйте forEach с деструктуризацией или цикл for с парой. Если нужно преобразовать карту — применяйте mapKeys и mapValues. Это самый идиоматичный Kotlin-код.

<<>>

Альтернативный подход: Использование Java-стиля для мигрантов

Если вы только перешли с Java и чувствуете себя неуверенно в функциональном стиле, можете сначала использовать привычные методы, а затем постепенно переходить на Kotlin-идиомы. Вот как выглядит "гибридный" подход:

val map = hashMapOf("a" to 1, "b" to 2, "c" to 3)

// Java-стиль через entrySet()
for (entry in map.entries) {
    println("${entry.key} = ${entry.value}")
}

// Только ключи
for (key in map.keys) {
    println("Ключ: $key")
}

// Только значения
for (value in map.values) {
    println("Значение: $value")
}

Когда это полезно? Если вы пишете код, который будет читать Java-разработчик, или если вам нужно выполнить сложные операции с итератором (например, удаление элементов во время обхода).

Недостаток: Этот код менее лаконичен и не использует всю мощь Kotlin (деструктуризацию, цепочки вызовов). Но как первый шаг — вполне нормально. Со временем вы привыкнете к forEach и map, и будете писать меньше кода с тем же результатом.

Также помните, что HashMap в Kotlin — это просто обёртка над Java-классом java.util.HashMap, поэтому все Java-методы также доступны.