Avatar ·

Почему в Kotlin у List нет методов add и remove, а у Map — put?

Всем привет! Я только начал изучать Kotlin, перешел с Java, и столкнулся с непонятной ситуацией. В Java мы привыкли работать с ArrayList, HashMap и спокойно вызывать list.add(), map.put() и т.д. Но в Kotlin, когда я пишу: ```kotlin val myList = listOf(1, 2, 3) myList.add(4) // Ошибка: Unresolved reference: add val myMap = mapOf("a" to 1, "b" to 2) myMap.put("c", 3) // Ошибка: Unresolved reference: put ``` Компилятор ругается, что таких методов нет. Я пробовал использовать mutableListOf и mutableMapOf — там add и put работают. Но почему для обычных listOf и mapOf их нет? Это баг языка? Или я что-то не понимаю в философии Kotlin? Подскажите, пожалуйста, как правильно работать с коллекциями в Kotlin, если мне нужно то добавлять элементы, то просто читать данные? И что вообще значат эти "mutable" и "immutable"? Заранее спасибо! <<>>

Короткий ответ: разница между изменяемыми и неизменяемыми коллекциями

В Kotlin коллекции разделены на два типа: изменяемые (mutable) и неизменяемые (immutable/read-only). Это не баг, а осознанное дизайнерское решение, которое делает код безопаснее и предсказуемее.

Функции listOf() и mapOf() создают неизменяемые коллекции. У них нет методов add, remove, put, потому что они не предназначены для изменения. Это гарантирует, что коллекция останется той же самой на всём протяжении её использования.

Как правильно: используйте mutable-версии

Если вам нужно изменять коллекцию, используйте mutableListOf(), mutableMapOf() и т.д.:

val mutableList = mutableListOf(1, 2, 3)
mutableList.add(4)        // Работает
mutableList.removeAt(0)   // Работает

val mutableMap = mutableMapOf("a" to 1, "b" to 2)
mutableMap.put("c", 3)    // Работает
// Или более идиоматичный способ:
mutableMap["d"] = 4

Почему это важно?

  • Безопасность потоков: неизменяемые коллекции можно безопасно передавать между потоками — никто случайно их не изменит.
  • Предсказуемость: если функция принимает List<Int>, вы знаете, что она не испортит вашу коллекцию.
  • Функциональный стиль: Kotlin поощряет работу с данными через трансформации (map, filter и т.д.), а не мутацию.

Как работать с неизменяемыми коллекциями, если нужно "добавить" элемент?

Вместо мутации используйте операторы + и -, которые создают новую коллекцию:

val original = listOf(1, 2, 3)
val newList = original + 4          // [1, 2, 3, 4]
val smallerList = original - 2      // [1, 3] — удаляет первое вхождение 2

val map = mapOf("a" to 1, "b" to 2)
val newMap = map + ("c" to 3)       // {"a":1, "b":2, "c":3}
val withoutA = map - "a"            // {"b":2}

Исходная коллекция original остаётся неизменной — это ключевой принцип.

Совет для новичков

Начинайте с listOf и mapOf по умолчанию. Переходите на mutableListOf только когда точно знаете, что изменение необходимо. Со временем вы привыкнете к функциональному стилю — это сделает ваш код чище и надёжнее.

Полная иерархия:

  • Iterable → Collection → List / Set
  • MutableIterable → MutableCollection → MutableList / MutableSet
  • Map и MutableMap — отдельное семейство
<<>>

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

Если вам часто нужно "добавить" элемент в неизменяемый список, можно написать свою функцию-расширение:

fun <T> List<T>.with(element: T): List<T> = this + element
fun <T> List<T>.without(element: T): List<T> = this - element

// Использование:
val list = listOf(1, 2, 3)
val updated = list.with(4)    // [1, 2, 3, 4]
val removed = list.without(2) // [1, 3]

Также помните, что toMutableList() и toMutableMap() позволяют легко конвертировать неизменяемую коллекцию в изменяемую, если это нужно на короткое время:

val immutable = listOf(1, 2, 3)
val mutable = immutable.toMutableList()
mutable.add(4)
val backToImmutable = mutable.toList() // снова неизменяемый

Но старайтесь избегать частых конвертаций — это создаёт лишние копии и может снижать производительность. Лучше сразу выбирать правильный тип коллекции под задачу.