Вступление: Парадокс пустого списка в Kotlin
Представьте: вы пишете на Kotlin, и вам нужно создать список, который будет динамически пополняться. Вы знаете, что List — это неизменяемая коллекция, а MutableList — то, что доктор прописал. Но как только вы пишете val list = MutableList(), компилятор выдаёт ошибку. Знакомо?
Этот вопрос — один из самых популярных на Stack Overflow среди новичков в Kotlin (и не только). Тысячи разработчиков спотыкаются об одну и ту же граблю: как правильно инициализировать пустой MutableList? Казалось бы, мелочь, но без этого не работает ни один динамический алгоритм.
В этой статье мы разберём все легальные способы создать пустой изменяемый список в Kotlin. От простых лайфхаков до продвинутых техник. Вы узнаете, почему mutableListOf() — ваш лучший друг, а ArrayList() — старый, но надёжный товарищ. Поехали!
1. Почему просто MutableList() не работает?
Давайте сразу развеем миф. Если вы попробуете написать:
// Ошибка компиляции!
val list = MutableList()
Компилятор справедливо возмутится. Почему? Потому что MutableList — это интерфейс, а не конкретный класс. Интерфейс нельзя инстанцировать. Это как пытаться создать "просто автомобиль" без указания марки и модели. Kotlin требует конкретной реализации.
К счастью, у нас есть несколько элегантных способов обойти это ограничение.
2. Самый простой способ: mutableListOf()
Это король пустых списков. Стандартная функция-фабрика из стандартной библиотеки Kotlin:
val emptyList = mutableListOf<String>()
println(emptyList) // Вывод: []
println(emptyList.size) // Вывод: 0
Почему это круто:
- Не нужно указывать тип, если он может быть выведен из контекста
- Создаёт
MutableListс типомArrayListпод капотом - Читается как "создай мне изменяемый список из ничего"
Добавлять элементы — одно удовольствие:
val numbers = mutableListOf<Int>()
numbers.add(1)
numbers.add(2)
numbers.add(3)
println(numbers) // [1, 2, 3]
Этот метод подходит для 90% случаев. Запомните его как основной.
3. Классический ArrayList() — возвращение короля
Если вы пришли из Java, этот способ покажется вам родным. Kotlin полностью совместим с Java Collections Framework, поэтому вы можете использовать ArrayList напрямую:
val list = ArrayList<String>()
println(list) // []
Но есть нюанс: ArrayList — это конкретная реализация интерфейса MutableList. Если в дальнейшем вы захотите поменять реализацию (например, на LinkedList), придётся менять тип переменной. В этом смысле mutableListOf() гибче, так как возвращает MutableList.
Когда использовать ArrayList():
- Когда нужна максимальная производительность (ArrayList быстрее LinkedList для доступа по индексу)
- Когда вы работаете с Java-библиотеками, ожидающими именно ArrayList
- Когда вы сознательно выбираете конкретную реализацию
4. Хитрый способ: MutableList(size) { defaultValue }
Иногда пустой список — это не совсем "пустой". Бывает, нужно создать список с заранее известным размером, но с пустыми значениями. Для этого есть перегрузка конструктора:
// Создаём список из 5 элементов, каждый — пустая строка
val list = MutableList(5) { "" }
println(list) // [, , , , ]
println(list.size) // 5
Это не совсем пустой список, но список, инициализированный "пустышками". Полезно, когда вы точно знаете количество элементов, но будете их перезаписывать.
Для полного нуля используйте mutableListOf(). А этот способ — для ситуаций "заполню потом".
5. Пустой список через конструктор с коллекцией
Ещё один способ — передать пустую коллекцию в конструктор:
val emptyList = mutableListOf<String>().toMutableList()
// или
val anotherEmpty = ArrayList(emptyList<String>())
Но это уже извращение. Зачем создавать пустой список, чтобы скопировать его в другой пустой список? Разве что для демонстрации возможностей. В реальном коде так делать не стоит.
6. Сравнение способов: что выбрать?
Давайте сведём все варианты в таблицу (мысленно, конечно):
- mutableListOf() — универсальный выбор, рекомендуется для 90% случаев
- ArrayList() — когда нужна конкретная реализация или совместимость с Java
- MutableList(size) {} — когда нужно создать список фиксированного размера с дефолтными значениями
- Конструктор с коллекцией — только для учебных примеров
Мой личный фаворит — mutableListOf(). Он короткий, понятный и не привязывает к конкретной реализации.
7. Частые ошибки новичков
Давайте разберём грабли, на которые наступают почти все:
Ошибка 1: Использование listOf() вместо mutableListOf()
val list = listOf<String>() // Это неизменяемый список!
list.add("Hello") // Ошибка компиляции!
listOf() создаёт List, а не MutableList. Если вы планируете изменять список — используйте mutableListOf().
Ошибка 2: Забыли указать тип
val list = mutableListOf() // Ошибка: не удаётся вывести тип
list.add(1)
list.add("строка") // Ошибка: тип не соответствует
Kotlin строго типизирован. Если не указать тип, компилятор не сможет понять, какие элементы вы собираетесь хранить. Всегда указывайте тип в угловых скобках или в контексте, где его можно вывести:
val list = mutableListOf<Int>() // Правильно
list.add(1) // Ок
Ошибка 3: Путаница между MutableList и List в сигнатурах функций
fun process(list: MutableList<Int>) { // Требует изменяемый список
list.add(1)
}
fun main() {
val immutableList = listOf(1, 2, 3)
process(immutableList) // Ошибка: типы не совпадают
}
Если ваша функция изменяет список, принимайте