Похоже, что Vector
опоздал на вечеринку коллекций Scala, и все влиятельные посты в блоге уже ушли.
В Java ArrayList
это коллекция по умолчанию - я мог бы использовать, LinkedList
но только когда я продумал алгоритм и достаточно осторожен, чтобы оптимизировать. В Scala я должен использовать Vector
по умолчанию Seq
или пытаться работать, когда List
это на самом деле более уместно?
scala
vector
scala-collections
Дункан МакГрегор
источник
источник
List<String> l = new ArrayList<String>()
блоги для написания Scala, чтобы вы поверили, что все используют List для того, чтобы получить постоянную коллекционную ценность, но достаточно ли общего назначения Vector, чтобы мы могли использовать его вместо List?List
когда яSeq()
печатаю в REPL.IndexedSeq
.Seq
старше трех лет. Начиная с версии Scala 2.11.4 (и ранее), конкретный тип по умолчаниюSeq
- этоList
.Ответы:
Как правило, по умолчанию используется
Vector
. Это быстрее, чемList
для почти всего, и более эффективно для памяти для последовательностей, размер которых превышает тривиальный. См. Эту документацию относительно производительности Вектор по сравнению с другими коллекциями. Есть некоторые недостатки, чтобы идти сVector
. В частности:List
(хотя и не так сильно, как вы думаете)Еще одним недостатком до Scala 2.10 было то, что поддержка сопоставления с образцом была лучше
List
, но это было исправлено в 2.10 с обобщенным+:
и:+
экстракторами.Существует также более абстрактный, алгебраический подход к этому вопросу: какая последовательность у вас концептуально ? Кроме того, что ты концептуально делаешь с этим? Если я вижу функцию, которая возвращает an
Option[A]
, я знаю, что функция имеет некоторые дыры в своей области (и, следовательно, является частичной). Мы можем применить ту же логику к коллекциям.Если у меня есть последовательность типов
List[A]
, я эффективно утверждаю две вещи. Во-первых, мой алгоритм (и данные) полностью структурированы. Во-вторых, я утверждаю, что единственное, что я собираюсь сделать с этой коллекцией, это полные, O (n) обходы. Эти двое действительно идут рука об руку. И наоборот, если у меня есть что-то типаVector[A]
, единственное, что я утверждаю, это то, что мои данные имеют четко определенный порядок и конечную длину. Таким образом, утверждения слабееVector
, и это приводит к его большей гибкости.источник
case head +: tail
илиcase tail :+ head
. Чтобы соответствовать пустым, вы можете сделатьcase Seq()
и так далее. Все, что вам нужно, есть в API, который является более универсальным, чемList
sList
реализован с помощью односвязного списка.Vector
реализован что-то вроде JavaArrayList
.Ну, а
List
может быть невероятно быстрым, если алгоритм может быть реализован исключительно с::
,head
иtail
. У меня был наглядный урок этого совсем недавно, когда я победил Java,split
генерируяList
вместоArray
, и не смог побить это ни с чем другим.Однако
List
есть фундаментальная проблема: он не работает с параллельными алгоритмами. Я не могу эффективно разбить егоList
на несколько сегментов или объединить обратно.Существуют другие виды коллекций, которые могут гораздо лучше справляться с параллелизмом, и
Vector
это одна из них.Vector
также имеет отличную локализацию - чтоList
нет - что может быть реальным плюсом для некоторых алгоритмов.Итак, учитывая все обстоятельства,
Vector
это лучший выбор если у вас нет особых соображений, которые делают одну из других коллекций предпочтительной - например, вы можете выбратьStream
, хотите ли вы ленивую оценку и кэширование (Iterator
быстрее, но не кэширует), илиList
если алгоритм естественным образом реализован с помощью операций, которые я упомянул.Кстати, предпочтительно использовать
Seq
либо,IndexedSeq
если вы не хотите конкретную часть API (напримерList
, s::
), или дажеGenSeq
илиGenIndexedSeq
если ваш алгоритм может работать параллельно.источник
Vector
такое неизменяемая структура данных в Scala?Некоторые из приведенных здесь утверждений сбивают с толку или даже ошибочны, особенно идея о том, что immutable.Vector в Scala - это что-то вроде ArrayList. List и Vector являются неизменными, постоянными (то есть «дешевыми, чтобы получить измененную копию») структурами данных. Нет разумного выбора по умолчанию, так как они могут быть для изменяемых структур данных, но это скорее зависит от того, что делает ваш алгоритм. List - это односвязный список, а Vector - целочисленное дерево с целым числом 32, т. Е. Это своего рода дерево поиска с узлами степени 32. Используя эту структуру, Vector может предоставлять наиболее распространенные операции достаточно быстро, т. Е. В O (log_32 ( п)). Это работает для prepend, append, update, произвольного доступа, декомпозиции в голове / хвосте. Итерация в последовательном порядке является линейной. Список, с другой стороны, просто обеспечивает линейную итерацию и постоянное предварительное добавление, разложение в голове / хвосте.
Это может выглядеть так, как будто Vector был хорошей заменой List почти во всех случаях, но prepend, декомпозиция и итерация часто являются критическими операциями над последовательностями в функциональной программе, и константы этих операций (намного) выше для вектора из-за к его более сложной структуре. Я провел несколько измерений, поэтому итерация для списка примерно в два раза быстрее, для списков предварительная обработка в списках примерно в 100 раз, для списков разложение в голове / хвосте в списках примерно в 10 раз, а генерация из перемещаемых объектов - для векторов примерно в 2 раза быстрее. (Вероятно, это связано с тем, что Vector может выделять массивы из 32 элементов одновременно, когда вы создаете его с помощью компоновщика вместо того, чтобы добавлять или добавлять элементы один за другим).
Так какую структуру данных мы должны использовать? В основном, есть четыре распространенных случая:
источник
Для неизменяемых коллекций, если вы хотите последовательность, ваше основное решение - использовать ли
IndexedSeq
или aLinearSeq
, которые дают разные гарантии производительности. IndexedSeq обеспечивает быстрый произвольный доступ к элементам и быструю операцию длины. LinearSeq обеспечивает быстрый доступ только к первому элементу черезhead
, но также имеет быструюtail
работу. (Взято из документации Seq.)Для
IndexedSeq
вы обычно выбираетеVector
.Range
с иWrappedString
s также являются IndexedSeqs.Для a
LinearSeq
вы обычно выбираете егоList
или его ленивый эквивалентStream
. Другими примерами являютсяQueue
s иStack
s.Таким образом, в терминах Java
ArrayList
используется аналогично ScalaVector
иLinkedList
аналогично ScalaList
. Но в Scala я бы предпочел использовать List чаще, чем Vector, потому что Scala гораздо лучше поддерживает функции, включающие обход последовательности, такие как отображение, свертывание, итерации и т. Д. Вы будете стремиться использовать эти функции для управления списком как в целом, а не случайный доступ к отдельным элементам.источник
Vector
«S итерация это быстрее, но кто - то должен его эталонное тестирование , чтобы убедиться.Vector
физически существуют вместе в ОЗУ в группах по 32, которые более полно помещаются в кэш-память ЦП ... так что меньше кэш-пропусковВ ситуациях, которые включают много случайного доступа и случайных мутаций,
Vector
(или, как говорят в документации - аSeq
), кажется хорошим компромиссом. Это также то, что показывают характеристики производительности .Кроме того,
Vector
кажется , что класс хорошо работает в распределенных средах без большого дублирования данных, потому что нет необходимости выполнять копирование при записи для всего объекта. (См .: http://akka.io/docs/akka/1.1.3/scala/stm.html#persistent-datastructures )источник
IndexedSeq
. Что тожеVector
, но это другое дело.IndexedSeq
который реализуетSeq
.Seq(1, 2, 3)
этоLinearSeq
который реализован с помощьюList
.Если вы постоянно программируете и нуждаетесь в произвольном доступе, Seq - это то, что вам нужно (если только вы не хотите использовать Set, который вы часто делаете). В противном случае список работает хорошо, за исключением того, что его операции нельзя распараллелить.
Если вам не нужны неизменяемые структуры данных, используйте ArrayBuffer, поскольку это Scala-эквивалент ArrayList.
источник