В чём разница между immutable и mutable коллекциями?
Разница между immutable и mutable коллекциями в Scala заключается в способности коллекции изменять своё содержимое после создания. Это различие отражает базовые принципы функционального и императивного программирования.
Immutable коллекции
Immutable (неизменяемые) коллекции нельзя изменить после создания. Все операции, которые выглядят как изменения (например, добавление элемента), возвращают новую коллекцию, при этом оригинальная остаётся неизменной.
Примеры:
val list = List(1, 2, 3)
val newList = list :+ 4 // создаётся новый список List(1, 2, 3, 4)
Основные характеристики:
-
Потокобезопасность (thread-safe) без синхронизации.
-
Лучше подходят для параллельных вычислений.
-
Идеальны в функциональном программировании.
-
Объекты не изменяются, можно делиться ими без риска.
Типичные структуры:
-
List, Vector, Map, Set, Range, Stream, Queue, Stack, Seq (из scala.collection.immutable)
-
Значения создаются и работают по принципу «новый объект вместо изменения».
Пример:
val set1 = Set(1, 2, 3)
val set2 = set1 + 4 // set1 остаётся прежним
Mutable коллекции
Mutable (изменяемые) коллекции могут изменяться: можно добавлять, удалять или изменять элементы на месте, без создания новой структуры.
Примеры:
val buffer = scala.collection.mutable.ListBuffer(1, 2, 3)
buffer += 4 // изменяется исходный buffer
Основные характеристики:
-
Требуют осторожности при использовании в многопоточном коде.
-
Часто быстрее в императивных сценариях.
-
Могут быть эффективнее, если нужно выполнять множество модификаций.
Типичные структуры:
- scala.collection.mutable.ListBuffer, ArrayBuffer, HashMap, HashSet, Queue, Stack, Array, BitSet, LinkedHashMap
Пример:
val map = scala.collection.mutable.Map("a" -> 1)
map("b") = 2 // изменили исходный map
Подключение нужной версии
Scala делит стандартные коллекции на три пространства имён:
Пространство | Описание |
---|---|
scala.collection | Общее (родительское) пространство |
--- | --- |
scala.collection.immutable | Все коллекции по умолчанию неизменяемые |
--- | --- |
scala.collection.mutable | Все коллекции изменяемые |
--- | --- |
Можно указать нужную имплементацию:
import scala.collection.mutable.ListBuffer
Сравнение по основным признакам
Признак | Immutable коллекции | Mutable коллекции |
---|---|---|
Изменяемость | Нет | Да |
--- | --- | --- |
Потокобезопасность | Высокая, не требуют синхронизации | Низкая, требуют явной защиты |
--- | --- | --- |
Производительность | Хорошая при небольшом количестве изменений | Эффективнее при множественных изменениях |
--- | --- | --- |
Поддержка в Scala | По умолчанию используются | Требуют явного импорта |
--- | --- | --- |
Совместимость с FP | Отличная | Ограниченная |
--- | --- | --- |
Семантика операций | Возвращают новые коллекции | Модифицируют текущую коллекцию |
--- | --- | --- |
Примеры коллекций | List, Map, Set, Vector, Range | ListBuffer, ArrayBuffer, HashMap, Array |
--- | --- | --- |
Когда использовать какую
-
Immutable коллекции подходят для:
-
Функционального кода
-
Больше читаемости и предсказуемости
-
Безопасного совместного использования
-
Параллельной обработки
-
-
Mutable коллекции подходят для:
-
Императивного, процедурного стиля
-
Быстрой модификации в циклах
-
Временных буферов, алгоритмов на массивах
-
Пример: сравнение List и ListBuffer
// Immutable List
val list = List(1, 2, 3)
val updated = 0 :: list // создаёт новый список
// Mutable ListBuffer
val buf = scala.collection.mutable.ListBuffer(1, 2, 3)
buf += 4 // изменяет существующий объект
Смешивание
Scala позволяет использовать обе парадигмы — функциональную (immutable) и императивную (mutable) — в рамках одного проекта. Однако в крупных проектах и библиотеках по умолчанию используется immutable-подход, как более безопасный и чистый.
Идиомы и стилистика
-
Функции стандартной библиотеки (например, map, filter, fold, reduce) всегда возвращают новые коллекции, независимо от того, immutable они или mutable.
-
Mutable коллекции часто используются в алгоритмах, где важна производительность и много мутаций, но результат обычно преобразуется в immutable на выходе:
val buf = scala.collection.mutable.ListBuffer\[Int\]()
for (i <- 1 to 100) buf += i
val result: List\[Int\] = buf.toList
Это позволяет совмещать эффективность с безопасностью и читаемостью.