Как преобразовать список (List) в словарь (Map) в Kotlin: полное руководство
Основные способы преобразования списка в словарь
В Kotlin есть несколько элегантных способов преобразовать List в Map. Самый популярный и идиоматичный — использование функции-расширения associateBy.
1. Простое преобразование с помощью associateBy
Это самый простой способ, если вы хотите, чтобы ключом было поле объекта, а значением — сам объект:
data class User(val id: Int, val name: String)
val users = listOf(
User(1, "Анна"),
User(2, "Борис"),
User(3, "Виктор")
)
val userMap: Map<Int, User> = users.associateBy { it.id }
// Результат: {1=User(id=1, name=Анна), 2=User(id=2, name=Борис), 3=User(id=3, name=Виктор)}
Функция associateBy принимает лямбду, которая извлекает ключ из элемента списка. Всё остальное она делает за вас.
2. Использование map + toMap
Ваш вариант тоже рабочий, хотя и чуть более многословный:
val userMap = users.map { it.id to it }.toMap()
Здесь мы сначала создаём список пар Pair<Int, User>, а затем преобразуем его в Map. Этот подход полезен, если вам нужно преобразовать значение перед помещением в словарь.
3. Преобразование с помощью associate
Если вам нужно изменить и ключ, и значение, используйте associate:
val nameMap: Map<Int, String> = users.associate { it.id to it.name }
// Результат: {1=Анна, 2=Борис, 3=Виктор}
4. Обработка дублирующихся ключей
Если есть вероятность, что ключи повторяются, используйте associateBy с функцией слияния значений. Второй параметр — лямбда, определяющая, какое значение оставить при конфликте:
val usersWithDuplicates = listOf(
User(1, "Анна"),
User(2, "Борис"),
User(1, "Алексей") // дубликат ключа 1
)
// Оставляем первое значение (по умолчанию)
val mapKeepFirst = usersWithDuplicates.associateBy { it.id }
// Результат: {1=User(id=1, name=Анна), 2=User(id=2, name=Борис)}
// Оставляем последнее значение
val mapKeepLast = usersWithDuplicates.associateBy(
keySelector = { it.id },
valueTransform = { it }
)
// Результат: {1=User(id=1, name=Алексей), 2=User(id=2, name=Борис)}
// Кастомная логика: объединяем имена
val mergedMap = usersWithDuplicates.associateBy(
keySelector = { it.id },
valueTransform = { it }
).toMutableMap().apply {
// Если нужно объединить — используем groupBy
}
5. Группировка с помощью groupBy
Если у вас несколько элементов с одинаковым ключом, и вы хотите сохранить все, используйте groupBy. Он возвращает Map<K, List<V>>:
val groupedUsers: Map<Int, List<User>> = usersWithDuplicates.groupBy { it.id }
// Результат: {1=[User(id=1, name=Анна), User(id=1, name=Алексей)], 2=[User(id=2, name=Борис)]}
6. Альтернатива с toMap и функцией слияния
Если вы предпочитаете стиль Java Stream API, можно использовать toMap с явным merge-параметром:
val mapWithMerge = usersWithDuplicates.map { it.id to it.name }
.toMap(
mergeFunction = { existing, replacement -> "$existing, $replacement" }
)
// Результат: {1=Анна, Алексей, 2=Борис}
Рекомендация
- Используйте
associateByдля простого преобразования "ключ → объект". - Используйте
associate, когда нужно изменить и ключ, и значение. - Используйте
groupBy, если возможны дубликаты ключей и нужно сохранить все значения. - Избегайте
toMapбез необходимости — он создаёт промежуточный список пар.
Все эти методы являются ленивыми (возвращают новую коллекцию) и не изменяют исходный список. Они также потокобезопасны для неизменяемых коллекций.
<<Использование fold или runningFold для кастомной логики
Если стандартные функции не подходят (например, нужно накапливать значения или выполнять сложную обработку), можно использовать fold:
val userMap = users.fold(mutableMapOf<Int, User>()) { acc, user ->
acc[user.id] = user
acc
}
Этот метод даёт полный контроль над процессом, но менее идиоматичен. Он полезен, когда нужно:
- Вести подсчёт или агрегацию данных.
- Изменить тип коллекции на
LinkedHashMapдля сохранения порядка. - Применить сложную логику разрешения конфликтов.
Производительность
Для больших списков (более 10 000 элементов) associateBy и groupBy работают одинаково быстро — O(n). fold может быть чуть медленнее из-за создания промежуточных объектов, но разница обычно незаметна.
Важное замечание
Всегда помните о неизменяемости: стандартные функции возвращают Map, а не MutableMap. Если вам нужен изменяемый словарь, используйте .toMutableMap() в конце цепочки.