Когда использовать 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 и других методов преобразования, что делает выбор структуры гибким и адаптируемым под задачи.