Когда использовать Vector, List, Seq, Array, Map?
В Scala существует множество коллекций, и выбор между Vector, List, Seq, Array, Map зависит от нескольких факторов: производительности операций (доступ, вставка, удаление), иммутабельности, стиля программирования (функциональный или императивный), а также от семантики задачи. Ниже приведено подробное объяснение каждой структуры, а также случаи, когда она наиболее уместна.
List
List — это неизменяемая (immutable), односвязная структура данных. Добавление в начало (::) происходит за O(1), но доступ к элементам по индексу — O(n).
Особенности:
-
Идеален для последовательной обработки (итерация, рекурсия).
-
Быстро добавлять элементы в начало списка.
-
Нельзя быстро получить элемент по индексу.
-
Поддерживает ленивую голову (head) и хвост (tail).
Когда использовать:
-
Когда требуется функциональный стиль и последовательная обработка.
-
Если вы часто добавляете элементы в начало.
-
Когда индексированный доступ не важен.
Пример:
val list = List(1, 2, 3)
val newList = 0 :: list // List(0, 1, 2, 3)
Vector
Vector — это неизменяемая коллекция с быстрой навигацией по элементам. Построена на основе префиксного дерева (32-арного), что обеспечивает логарифмическую глубину (очень малую).
Особенности:
-
Быстрый индексированный доступ — почти O(1).
-
Более эффективен, чем List, при больших размерах и случайном доступе.
-
Медленнее, чем Array, но безопаснее (immutable).
-
Добавление в конец и начало работает за O(log32(n)).
Когда использовать:
-
Если нужен универсальный, сбалансированный список.
-
Когда важны эффективность и функциональность.
-
Когда работа с большими коллекциями требует частого доступа по индексу.
Пример:
val vector = Vector(1, 2, 3)
val updated = vector.updated(1, 10) // Vector(1, 10, 3)
Seq
Seq — это абстрактный тип, общий для всех последовательностей. Это интерфейс, а не конкретная реализация.
Особенности:
-
Представляет упорядоченную коллекцию элементов.
-
Поддерживает индексацию, итерации, трансформации (map, filter).
-
Может быть immutable или mutable (immutable.Seq, mutable.Seq).
-
Реализация по умолчанию для immutable.Seq — это List, но можно явно указать Vector.
Когда использовать:
-
Когда нужна абстракция: функция может принимать любую последовательность.
-
В API и библиотечном коде, где не важна реализация.
-
Для обобщённых методов, работающих с разными типами коллекций.
Пример:
def process(seq: Seq\[Int\]): Int = seq.sum
Array
Array — это изменяемый, низкоуровневый контейнер, близкий к массивам в Java. Хранит элементы в непрерывном блоке памяти.
Особенности:
-
Быстрый доступ по индексу — O(1).
-
Эффективен по памяти.
-
Изменяемый (можно менять значения на месте).
-
Не интегрируется напрямую с функциональным стилем (mutable), но может быть преобразован в Seq.
Когда использовать:
-
Для низкоуровневых оптимизаций и численных вычислений.
-
Когда важна максимальная производительность.
-
В случае, когда количество элементов известно и фиксировано.
Пример:
val arr = Array(1, 2, 3)
arr(1) = 10 // arr: Array(1, 10, 3)
Map
Map — отображение (dictionary) ключ → значение. Бывает mutable и immutable.
Особенности:
-
Быстрый доступ по ключу — почти O(1) для HashMap, O(log n) для TreeMap.
-
immutable.Map возвращает новый объект при вставке/удалении.
-
Поддерживает множество удобных операций (get, getOrElse, updated, map, filter и т.д.).
Когда использовать:
-
Когда нужно хранить и быстро искать по ключу.
-
Для конфигурационных и ассоциативных данных.
-
Если нужен безопасный (immutable) или производительный (mutable) вариант.
Пример:
val map = Map("a" -> 1, "b" -> 2)
val updated = map + ("c" -> 3) // Map("a" -> 1, "b" -> 2, "c" -> 3)
Обобщенная таблица выбора
Коллекция | Иммутабельность | Быстрый доступ по индексу | Быстрая вставка | Случайный доступ | Лучшее применение |
---|---|---|---|---|---|
List | Да | Нет (O(n)) | В начало (O(1)) | Нет | Рекурсия, стек |
--- | --- | --- | --- | --- | --- |
Vector | Да | Да (почти O(1)) | Умеренно быстро | Да | Общего назначения |
--- | --- | --- | --- | --- | --- |
Seq | Зависит | Зависит | Зависит | Зависит | Абстракция |
--- | --- | --- | --- | --- | --- |
Array | Нет | Да (O(1)) | Да | Да | Оптимизация |
--- | --- | --- | --- | --- | --- |
Map | Да/Нет | По ключу (O(1) или O(log n)) | Да | Да | Ассоциативные данные |
--- | --- | --- | --- | --- | --- |
Рекомендации по применению
-
Используйте List для функционального, одностороннего прохода.
-
Используйте Vector как универсальный список с быстрым доступом.
-
Используйте Array, если работаете с большими числовыми массивами в циклах или математике.
-
Используйте Seq, если вы пишете универсальный интерфейс и не хотите привязываться к конкретной реализации.
-
Используйте Map, если необходимо ассоциировать ключи и значения и выполнять быстрые lookups.
Scala позволяет легко переключаться между этими структурами с помощью .toList, .toVector, .toArray, .toMap и других методов преобразования, что делает выбор структуры гибким и адаптируемым под задачи.