Когда следует выбрать Vector в Scala?

кажется,Vector опоздал на вечеринку Scala collections, и все влиятельные блоги уже ушли.

В Java ArrayList коллекция по умолчанию - я мог бы использовать LinkedList но только когда я продумывал алгоритм и оптимизировать. В Scala я должен использовать Vector как по умолчанию Seq, или пытается работать, когда List на самом деле более уместно?

6 ответов


как правило, по умолчанию используется Vector. Это быстрее, чем List на почти все и более эффективная память для последовательностей большего размера, чем тривиальные. Смотрите это документация относительной производительности вектора по сравнению с другими коллекциями. Есть некоторые недостатки в работе с Vector. В частности:

  • обновления в голове медленнее, чем List (хотя и не так сильно, как вы могли бы думаю)

еще одним недостатком перед Scala 2.10 было то, что поддержка сопоставления шаблонов была лучше для List, но это было исправлено в 2.10 с генерализованными +: и :+ экстракторы.

существует также более абстрактный, алгебраический способ подхода к этому вопросу: какая последовательность у вас принципиально есть? Кроме того, что вы принципиально делаешь с ним? Если я вижу функцию, которая возвращает Option[A], Я знаю, что функция имеет некоторые отверстия в своей области (и, следовательно, является частичной). Мы можем применить ту же логику к коллекциям.

если у меня есть последовательность типа List[A], Я эффективно утверждаю две вещи. Во-первых, мой алгоритм (и данные) полностью структурирован стеком. Во-вторых, я утверждаю, что единственное, что я собираюсь сделать с этой коллекцией, - это полные, o(n) траверсы. Эти двое идут рука об руку. И наоборот, если у меня есть что-то типа Vector[A] на только что я утверждение состоит в том, что мои данные имеют четко определенный порядок и конечную длину. Таким образом, утверждения слабее с Vector, и это приводит к его большей гибкости.


Ну List может быть невероятно быстрым, если алгоритм может быть реализован исключительно с помощью ::, head и tail. У меня был объектный урок этого совсем недавно, когда я победил Java split при создании List вместо Array, и не мог победить эту ни с чем другим.

, List имеет фундаментальную проблему: он не работает с параллельными алгоритмами. Я не могу разделить List на несколько сегментов или объединить его обратно, в эффективной манера.

есть другие виды коллекций, которые могут обрабатывать параллелизм намного лучше - и Vector - один из них. Vector также имеет большую местность -- которая List не -- что может быть плюсом для некоторых алгоритмов.

Итак, учитывая все обстоятельства,Vector самый лучший выбор если у вас есть конкретные соображения, которые делают одну из других коллекций предпочтительной - например, вы можете выбрать Stream если вы хотите ленивостью и кэширование (Iterator быстрее, но не кэша), или List если алгоритм естественно реализован с операциями, которые я упомянул.

кстати, предпочтительнее использовать Seq или IndexedSeq если вы не хотите определенную часть API (например,List ' s ::), или даже GenSeq или GenIndexedSeq если ваш алгоритм можно запустить параллельно.


для неизменяемых коллекций, Если вы хотите последовательность, ваше главное решение-использовать IndexedSeq или LinearSeq, которые дают различные гарантии для представления. IndexedSeq обеспечивает быстрый случайный доступ элементов и быструю операцию длины. LinearSeq обеспечивает быстрый доступ только к первому элементу через head, но и быстро tail операции. (Взято из документации Seq.)

на IndexedSeq обычно вы выбираете Vector. Ranges и WrappedStrings также являются IndexedSeqs.

на LinearSeq обычно вы выбираете List или его ленивый эквивалент Stream. Другими примерами являются Queues и Stacks.

Итак, в терминах Java,ArrayList используется аналогично Scala Vector и LinkedList аналогично Scala List. Но в Scala я бы чаще использовал List, чем Vector, потому что Scala имеет гораздо лучшую поддержку функций, которые включают в себя обход последовательности, например отображение, сворачивание, итерацию так далее. Вы будете использовать эти функции для управления списком в целом, а не для случайного доступа к отдельным элементам.


некоторые из утверждений здесь запутаны или даже неправильны, особенно идея, которая неизменна.Вектор в Scala-это что-то вроде ArrayList. Список и вектор являются неизменяемыми, постоянными (т. е. "дешевыми для получения измененной копии") структурами данных. Нет разумного выбора по умолчанию, поскольку они могут быть для изменяемых структур данных, но это зависит от того, что делает ваш алгоритм. List-это односвязный список, а Vector-целое число base-32, т. е. это своего рода дерево поиска с узлы степени 32. Используя эту структуру, вектор может обеспечить наиболее распространенные операции достаточно быстро, т. е. в O(log_32 (n)). Это работает для добавления, добавления, обновления, произвольного доступа, декомпозиции в head / tail. Итерация в последовательном порядке линейна. Список, с другой стороны, просто обеспечивает линейную итерацию и постоянное время, декомпозицию в head/tail. Все остальное занимает в общем линейное время.

Это может выглядеть так, как будто вектор был хорошей заменой для списка почти во всех случаи, но добавление, декомпозиция и итерация часто являются решающими операциями над последовательностями в функциональной программе, а константы этих операций (намного) выше для вектора из-за его более сложной структуры. Я сделал несколько измерений, поэтому итерация примерно в два раза быстрее для list, prepend примерно в 100 раз быстрее в списках, декомпозиция в head/tail примерно в 10 раз быстрее в списках, а генерация из traversable примерно в 2 раза быстрее для векторов. (Это, вероятно,, потому что Vector может выделять массивы из 32 элементов сразу, когда вы строите его с помощью builder вместо добавления или добавления элементов один за другим). Конечно, все операции, которые занимают линейное время в списках, но фактически постоянное время на векторах (как случайный доступ или добавление), будут непомерно медленными в больших списках.

Итак, какую структуру данных мы должны использовать? В принципе, есть четыре распространенных случая:

  • нам нужно только преобразовать последовательности операциями, такими как карта, фильтр, створка etc: в принципе, это не имеет значения, мы должны программировать наш алгоритм в целом и, возможно, даже выиграем от принятия параллельных последовательностей. Для последовательных операций список, вероятно, немного быстрее. Но вы должны проверить его, если вам нужно оптимизировать.
  • нам нужно много произвольного доступа и различные обновления, поэтому мы должны использовать вектор, список будет слишком медленно.
  • мы работаем над списками классическим функциональным способом, создавая их путем добавления и итерация рекурсивной декомпозицией: используйте список, вектор будет медленнее в 10-100 раз или более.
  • у нас есть критический алгоритм производительности, который в основном императивен и делает много случайного доступа к списку, что-то вроде быстрой сортировки на месте: используйте императивную структуру данных, например ArrayBuffer, локально и скопируйте данные из и в него.

в ситуациях, которые включают в себя много случайного доступа и случайной мутации, a Vector (или – как docs сказать–Seq) кажется хорошим компромиссом. Это также то, что характеристики предлагаю.

и Vector класс, похоже, хорошо играет в распределенных средах без большого дублирования данных, потому что нет необходимости делать копию при записи для полного объекта. (Видеть: http://akka.io/docs/akka/1.1.3/scala/stm.html#persistent-datastructures)


Если вы программируете неизменно и нуждаетесь в случайном доступе, Seq-это путь (если вы не хотите набор, который вы часто делаете). В противном случае список работает хорошо, за исключением того, что его операции не могут быть распараллелены.

Если вам не нужны неизменяемые структуры данных, придерживайтесь ArrayBuffer, поскольку это Scala, эквивалентный ArrayList.