> Какие коллекции существуют в Kotlin и каковы их особенности (Kotlin, Android)

Уровень: senior · Роль: mobile · Категория: Технические вопросы

Компании: MTS, Ozon, Rutube, Dogma, Avito, Одноклассники, IBS

Стек: Kotlin, Android

> Пример ответа

Короткий ответ

В Kotlin коллекции делятся на неизменяемые (read-only) и изменяемые (mutable). Основные интерфейсы: List, Set, Map и их mutable-версии. Ключевая особенность - строгое разделение на чтение и запись на уровне типов, а также наличие как стандартных реализаций из Java (ArrayList, HashSet, HashMap), так и специализированных (ArrayDeque, LinkedHashSet). Также есть sequence для ленивых вычислений и операции вроде map, filter, fold.

Подробное объяснение

Kotlin предоставляет два набора интерфейсов:

  • Read-only: List<T>, Set<T>, Map<K, V> - гарантируют отсутствие методов модификации, но не гарантируют неизменяемость (может быть приведена к mutable-версии).
  • Mutable: MutableList<T>, MutableSet<T>, MutableMap<K, V> - добавляют операции add, remove, put и т.д.

Реализации по умолчанию:

  • listOf() → ArrayList (на JVM)
  • setOf() → LinkedHashSet (сохраняет порядок вставки)
  • mapOf() → LinkedHashMap (сохраняет порядок)
  • mutableListOf() → ArrayList
  • mutableSetOf() → LinkedHashSet
  • mutableMapOf() → LinkedHashMap

Особенности:

  • Nullability: коллекции могут содержать null, если тип допускает (List<String?>).
  • Ковариантность: List<out T> - можно присвоить List<String> переменной типа List<Any>. MutableList<T> инвариантен.
  • Sequence: ленивые вычисления, полезны для больших данных или цепочек операций, где нужен short-circuit.
  • Операторы: filter, map, flatMap, groupBy, associate, zip, chunked, windowed и др.
  • Специализированные коллекции: ArrayDeque (двусторонняя очередь), IntArray, LongArray и т.д. для примитивов без boxing.
  • Java interop: коллекции Kotlin - это те же Java-коллекции, но с расширениями. java.util.ArrayList можно использовать напрямую.

На практике

На Android важно помнить:

  • Для UI-списков часто используют List из androidx.recyclerview с DiffUtil, но это не коллекция Kotlin, а адаптер.
  • При работе с LiveData или StateFlow лучше отдавать read-only коллекции наружу, чтобы избежать мутаций извне.
  • Для больших списков (например, при фильтрации) использовать asSequence() - иначе каждая операция создаёт промежуточный список.
  • groupBy и associate могут быть дорогими - оценивать сложность.
  • При передаче коллекций между потоками использовать Collections.unmodifiableList или toList() для копии.

Пример кода

val numbers = listOf(1, 2, 3, 4, 5)

// read-only
val doubled = numbers.map { it * 2 }

// mutable
val mutable = mutableListOf(1, 2)
mutable.add(3)

// sequence для ленивых вычислений
val result = numbers.asSequence()
    .filter { it % 2 == 0 }
    .map { it * 10 }
    .toList()

// groupBy
val grouped = numbers.groupBy { if (it % 2 == 0) "even" else "odd" }

// ковариантность
val strings: List<String> = listOf("a")
val anyList: List<Any> = strings

Как отвечать на собеседовании

Начни с разделения на read-only и mutable. Затем перечисли основные реализации и их особенности (порядок, производительность). Упомяни про sequence и ленивость. Приведи пример использования в Android-контексте - например, почему не стоит возвращать mutable-коллекцию из ViewModel. Если спросят про производительность, говори про сложность операций: ArrayList - O(1) доступ по индексу, LinkedList - O(n); HashSet - O(1) в среднем, но не гарантирует порядок.

Что проверяет интервьюер

  • Понимание разницы между read-only и immutable.
  • Знание стандартных реализаций и их trade-off.
  • Умение выбирать коллекцию под задачу (например, ArrayDeque для стека/очереди).
  • Понимание ленивых вычислений через Sequence.
  • Осознание особенностей Java interop и nullability.

Типичные ошибки

  • Путать read-only с immutable: val list = listOf(1) - нельзя добавить, но можно привести к mutable через toMutableList().
  • Использовать mutableListOf() по умолчанию, когда достаточно read-only.
  • Забывать про Sequence для больших данных - создаются лишние промежуточные списки.
  • Не учитывать, что setOf() и mapOf() сохраняют порядок вставки, в отличие от HashSet/HashMap.
  • Использовать List из java.util без учёта Kotlin-расширений (например, firstOrNull вместо find).

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью