Es scheint, dass Vector
es zu spät zur Scala-Sammlungsparty war und alle einflussreichen Blog-Beiträge bereits abgereist waren.
In Java ArrayList
ist die Standardsammlung - ich kann sie verwenden, LinkedList
aber nur, wenn ich einen Algorithmus durchdacht habe und mich genug um die Optimierung kümmere. Sollte ich in Scala Vector
meine Standardeinstellung verwenden Seq
oder versuchen, herauszufinden, wann dies List
tatsächlich angemessener ist?
scala
vector
scala-collections
Duncan McGregor
quelle
quelle
List<String> l = new ArrayList<String>()
Scala-Blogs schreiben würde. Würden Sie glauben, dass jeder List verwendet, um dauerhafte Sammlungsgüte zu erhalten - aber ist Vector allgemein genug, dass wir es anstelle von List verwenden sollten?List
wenn ichSeq()
bei REPL tippe.IndexedSeq
.Seq
ist über drei Jahre alt. Ab Scala 2.11.4 (und früher), der Standard - BetontypSeq
istList
.Antworten:
In der Regel standardmäßig verwenden
Vector
. Es ist schneller alsList
für fast alles und speichereffizienter für Sequenzen, die größer als trivial sind. In dieser Dokumentation wird die relative Leistung von Vector im Vergleich zu den anderen Sammlungen beschrieben. Es gibt einige NachteileVector
. Speziell:List
(wenn auch nicht so viel, wie Sie vielleicht denken)Ein weiterer Nachteil vor Scala 2.10 war, dass die Unterstützung für die Mustererkennung besser
List
war, dies wurde jedoch in 2.10 mit Generalized+:
und:+
Extractors behoben .Es gibt auch eine abstraktere, algebraischere Herangehensweise an diese Frage: Welche Art von Sequenz haben Sie konzeptionell ? Und was machst du konzeptionell damit? Wenn ich eine Funktion sehe, die eine zurückgibt
Option[A]
, weiß ich, dass diese Funktion einige Lücken in ihrer Domäne hat (und daher teilweise ist). Wir können dieselbe Logik auf Sammlungen anwenden.Wenn ich eine Sequenz von Typ habe
List[A]
, behaupte ich effektiv zwei Dinge. Erstens sind mein Algorithmus (und meine Daten) vollständig stapelstrukturiert. Zweitens behaupte ich, dass die einzigen Dinge, die ich mit dieser Sammlung machen werde, voll sind, O (n) Durchquerungen. Diese beiden gehen wirklich Hand in Hand. Umgekehrt, wenn ich etwas vom Typ habeVector[A]
, behaupte ich nur , dass meine Daten eine genau definierte Reihenfolge und eine endliche Länge haben. Somit sind die Behauptungen mit schwächerVector
, und dies führt zu seiner größeren Flexibilität.quelle
case head +: tail
odercase tail :+ head
. Um gegen leer zu spielen, können Sie tuncase Seq()
und so weiter. Alles, was Sie brauchen, ist in der API enthalten, die vielseitiger ist alsList
'sList
wird mit einer einfach verknüpften Liste implementiert.Vector
ist so etwas wie Java implementiertArrayList
.Nun, ein
List
kann unglaublich schnell sein , wenn der Algorithmus mit nur umgesetzt werden kann::
,head
undtail
. Ich hatte vor kurzem eine Objektstunde davon, als ich Javas schlug, indem ichsplit
einList
statt eines generierteArray
, und das mit nichts anderem schlagen konnte.Hat
List
jedoch ein grundlegendes Problem: Es funktioniert nicht mit parallelen Algorithmen. Ich kann a nichtList
effizient in mehrere Segmente aufteilen oder wieder verketten.Es gibt andere Arten von Sammlungen, die viel besser mit Parallelität umgehen können - und
Vector
eine davon.Vector
hat auch eine großartige Lokalität - wasList
nicht der Fall ist - was für einige Algorithmen ein echtes Plus sein kann.Alles in allem
Vector
ist dies die beste Wahl, es sei denn, Sie haben bestimmte Überlegungen, die eine der anderen Sammlungen vorzuziehen machen. Sie können beispielsweise auswählen,Stream
ob Sie eine verzögerte Auswertung und Zwischenspeicherung wünschen (Iterator
ist schneller, aber nicht zwischengespeichert) oderList
ob Der Algorithmus wird natürlich mit den von mir erwähnten Operationen implementiert.Übrigens ist es vorzuziehen, eine bestimmte API (z. B. 's ) zu verwenden
Seq
oder esIndexedSeq
sei denn, Sie möchten eine bestimmte API (z. B.List
' s::
) oder sogarGenSeq
oderGenIndexedSeq
wenn Ihr Algorithmus parallel ausgeführt werden kann.quelle
Vector
es sich bei Scala um eine unveränderliche Datenstruktur handelt?Einige der Aussagen hier sind verwirrend oder sogar falsch, insbesondere die Idee, dass unveränderlich. Vector in Scala ist so etwas wie eine ArrayList. List und Vector sind unveränderliche, persistente (dh "billig, um eine modifizierte Kopie zu erhalten") Datenstrukturen. Es gibt keine vernünftige Standardauswahl, wie sie für veränderbare Datenstrukturen gelten könnte, sondern es hängt vielmehr davon ab, was Ihr Algorithmus tut. List ist eine einfach verknüpfte Liste, während Vector eine Basis-32-Ganzzahl-Trie ist, dh eine Art Suchbaum mit Knoten des Grades 32. Mit dieser Struktur kann Vector die häufigsten Operationen relativ schnell bereitstellen, dh in O (log_32 ( n)). Das funktioniert für das Voranstellen, Anhängen, Aktualisieren, Direktzugriff und Zerlegen in Kopf / Schwanz. Die Iteration in sequentieller Reihenfolge ist linear. Die Liste hingegen bietet nur eine lineare Iteration und eine konstante Zeitvoraussetzung sowie eine Zerlegung in Kopf / Schwanz.
Dies mag so aussehen, als wäre Vektor in fast allen Fällen ein guter Ersatz für List, aber Voranstellen, Zerlegen und Iterieren sind häufig die entscheidenden Operationen für Sequenzen in einem Funktionsprogramm, und die Konstanten dieser Operationen sind für den fälligen Vektor (viel) höher zu seiner komplizierteren Struktur. Ich habe einige Messungen durchgeführt, sodass die Iteration für Listen etwa doppelt so schnell ist, das Voranstellen von Listen etwa 100-mal schneller ist, die Zerlegung in Kopf / Schwanz in Listen etwa 10-mal schneller ist und die Generierung aus einem Traversable für Vektoren etwa 2-mal schneller ist. (Dies liegt wahrscheinlich daran, dass Vector Arrays mit 32 Elementen gleichzeitig zuweisen kann, wenn Sie es mit einem Builder erstellen, anstatt Elemente einzeln voranzustellen oder anzuhängen.)
Welche Datenstruktur sollten wir also verwenden? Grundsätzlich gibt es vier häufige Fälle:
quelle
Wenn Sie für unveränderliche Sammlungen eine Sequenz wünschen, ist Ihre Hauptentscheidung, ob Sie eine
IndexedSeq
oder eine verwendenLinearSeq
, die unterschiedliche Leistungsgarantien bieten . Ein IndexedSeq bietet einen schnellen Direktzugriff auf Elemente und eine Operation mit schneller Länge. Ein LinearSeq bietet schnellen Zugriff nur auf das erste Element überhead
, hat aber auch einen schnellentail
Betrieb. (Entnommen aus der Seq-Dokumentation.)Für ein
IndexedSeq
würden Sie normalerweise eine wählenVector
.Range
s undWrappedString
s sind auch IndexedSeqs.Für a
LinearSeq
würden Sie normalerweise einList
oder sein faules Äquivalent wählenStream
. Andere Beispiele sindQueue
s undStack
s.Also in Java-Begriffen,
ArrayList
ähnlich wie bei ScalaVector
undLinkedList
ähnlich wie bei ScalaList
. In Scala würde ich List jedoch häufiger verwenden als Vector, da Scala Funktionen, die das Durchlaufen der Sequenz umfassen, wie Mapping, Folding, Iteration usw., viel besser unterstützt. Sie werden diese Funktionen tendenziell verwenden, um die Liste als zu bearbeiten ganz, anstatt zufällig auf einzelne Elemente zuzugreifen.quelle
Vector
‚s Iteration ist schneller, aber jemand Bedürfnisse Benchmark es sicher zu sein.Vector
RAM physisch in Gruppen von 32 zusammen existieren, die besser in den CPU-Cache passen ... also gibt es weniger Cache-In Situationen, die viel zufälligen Zugriff und zufällige Mutation beinhalten, scheint a
Vector
(oder - wie die Dokumente sagen - aSeq
) ein guter Kompromiss zu sein. Dies legen auch die Leistungsmerkmale nahe .Außerdem
Vector
scheint die Klasse in verteilten Umgebungen ohne große Datenverdoppelung gut zu spielen, da für das gesamte Objekt kein Copy-on-Write erforderlich ist. (Siehe: http://akka.io/docs/akka/1.1.3/scala/stm.html#persistent-datastructures )quelle
IndexedSeq
. Welches ist auchVector
, aber das ist eine andere Sache.IndexedSeq
die implementiert wirdSeq
.Seq(1, 2, 3)
ist eine,LinearSeq
die mit implementiert wirdList
.Wenn Sie unveränderlich programmieren und wahlfreien Zugriff benötigen, ist Seq der richtige Weg (es sei denn, Sie möchten ein Set, was Sie häufig tatsächlich tun). Andernfalls funktioniert List gut, außer dass die Operationen nicht parallelisiert werden können.
Wenn Sie keine unveränderlichen Datenstrukturen benötigen, bleiben Sie bei ArrayBuffer, da dies die Scala ist, die ArrayList entspricht.
quelle