Avatar ·

Как инициализировать пустой MutableList в Kotlin?

Всем привет! Я только начал изучать Kotlin после Java и столкнулся с простой, но запутанной проблемой.

Мне нужно создать пустой изменяемый список (MutableList), чтобы потом добавлять в него элементы. В Java я бы написал что-то вроде ArrayList list = new ArrayList<>();. Но в Kotlin я вижу много разных способов и не понимаю, какой из них правильный.

Вот что я пробовал:

// Вариант 1
var list1 = mutableListOf()

// Вариант 2
var list2: MutableList<String> = mutableListOf()

// Вариант 3
var list3 = emptyList<String>()

// Вариант 4
var list4 = ArrayList<String>()

Проблема в том, что:

  • emptyList() возвращает неизменяемый список, и я не могу добавить в него элементы.
  • mutableListOf() без указания типа работает, но потом я не могу добавить строки — пишет ошибку.
  • Вариант с ArrayList работает, но выглядит не по-котлиновски.

Подскажите, как правильно создать пустой MutableList, чтобы потом можно было добавлять в него элементы? И какой способ считается идиоматическим в Kotlin?

<<>>

Идиоматический способ: mutableListOf с указанием типа

Самый правильный и краткий способ создать пустой MutableList в Kotlin — использовать функцию mutableListOf() с явным указанием типа элементов:

val list = mutableListOf<String>()  // Создаёт пустой MutableList<String>
list.add("Kotlin")
list.add("Java")
println(list)  // [Kotlin, Java]

Если вы опустите тип, Kotlin не сможет его вывести из контекста, и вы получите MutableList<Nothing>, в который нельзя ничего добавить, кроме null:

val badList = mutableListOf()  // MutableList<Nothing>
// badList.add("Hello")  // Ошибка компиляции!
badList.add(null)  // Работает, но бесполезно

Поэтому всегда указывайте тип — либо в угловых скобках, либо в объявлении переменной:

// Вариант 1: явный тип в вызове
val list1: MutableList<String> = mutableListOf()

// Вариант 2: тип в угловых скобках (предпочтительно)
val list2 = mutableListOf<String>()

Почему не стоит использовать другие способы?

  • emptyList() — возвращает неизменяемый List. Вы не сможете вызвать add(), remove() и т.д. Он полезен, когда нужен только для чтения.

    val readOnly = emptyList<String>()
    // readOnly.add("test")  // Ошибка компиляции
    
  • ArrayList<String>() — работает, но это прямой вызов Java-класса. Kotlin рекомендует использовать свои фабричные функции для единообразия. Разницы в производительности нет, но код выглядит менее идиоматично.

  • listOf() — возвращает неизменяемый список. Даже если вы присвоите его переменной типа MutableList, это приведёт к ошибке компиляции или потребует приведения типов (что небезопасно).

Дополнительные возможности

Если вы хотите сразу создать список с элементами, используйте mutableListOf("a", "b", "c").

Для создания списка определённого размера с заполнением:

val list = MutableList(5) { index -> "Item $index" }
println(list)  // [Item 0, Item 1, Item 2, Item 3, Item 4]

Или пустой список с заданной ёмкостью (если знаете, сколько будет элементов):

val list = ArrayList<String>(100)  // Ёмкость 100, но список пуст

Однако для повседневного использования mutableListOf<T>() — это стандарт.

Итог

Запомните простое правило:

  • Нужен изменяемый список → mutableListOf<Тип>()
  • Нужен неизменяемый список → listOf<Тип>() или emptyList()

И всегда указывайте тип, чтобы избежать неожиданностей.

<<>>

Альтернативный подход: использование конструктора MutableList

В Kotlin есть ещё один способ создать пустой изменяемый список — через конструктор MutableList с указанием размера и лямбды-инициализатора:

val list = MutableList(0) { "" }  // Пустой список строк

Здесь первый аргумент — размер (0), второй — фабричная функция. Это работает, но выглядит избыточно для пустого списка.

Также можно использовать Java-стиль, если вы работаете с существующим Java-кодом:

val list = java.util.ArrayList<String>()  // Полный путь к классу

Но это не рекомендуется в чистом Kotlin-проекте.

Когда mutableListOf может не подойти?

Если вам нужно создать список с определённой начальной ёмкостью (например, вы знаете, что будет 1000 элементов), то mutableListOf создаёт список с ёмкостью по умолчанию (10). В таком случае лучше использовать:

val list = ArrayList<String>(1000)  // Избегает многократного расширения

Однако для 99% задач mutableListOf<T>() — это лучший выбор.