Il semble que Vector
c'était tard pour la soirée des collections Scala, et tous les articles de blog influents étaient déjà partis.
En Java, ArrayList
c'est la collection par défaut - je pourrais l'utiliser, LinkedList
mais seulement lorsque j'aurai réfléchi à un algorithme et que je me soucierai suffisamment pour l'optimiser. Dans Scala, dois-je utiliser Vector
par défaut Seq
ou essayer de déterminer quand List
est-ce réellement plus approprié?
scala
vector
scala-collections
Duncan McGregor
la source
la source
List<String> l = new ArrayList<String>()
créerais des blogs Scala pour vous faire croire que tout le monde utilise List pour obtenir une qualité de collection persistante - mais Vector est-il suffisamment polyvalent pour que nous devrions l'utiliser à la place de List?List
lorsque je tapeSeq()
à REPL.IndexedSeq
.Seq
plus de trois ans. Depuis Scala 2.11.4 (et versions antérieures), le type concret par défaut deSeq
estList
.Réponses:
En règle générale, utilisez par défaut
Vector
. C'est plus rapide queList
pour presque tout et plus efficace en mémoire pour les séquences de taille plus grande que triviale. Consultez cette documentation sur les performances relatives de Vector par rapport aux autres collections. Il y a des inconvénients à y allerVector
. Plus précisément:List
(mais pas autant que vous le pensez)Un autre inconvénient avant Scala 2.10 était que le support de correspondance de modèles était meilleur
List
, mais cela a été corrigé en 2.10 avec des généralisateurs+:
et des:+
extracteurs.Il existe également une manière plus abstraite et algébrique d'aborder cette question: quelle sorte de séquence avez-vous conceptuellement ? De plus, qu'en faites-vous conceptuellement ? Si je vois une fonction qui renvoie un
Option[A]
, je sais que cette fonction a quelques trous dans son domaine (et est donc partielle). Nous pouvons appliquer cette même logique aux collections.Si j'ai une séquence de type
List[A]
, j'affirme effectivement deux choses. Premièrement, mon algorithme (et mes données) est entièrement structuré en pile. Deuxièmement, j'affirme que les seules choses que je vais faire avec cette collection sont des traversées complètes O (n). Ces deux vont vraiment de pair. Inversement, si j'ai quelque chose de typeVector[A]
, la seule chose que j'affirme, c'est que mes données ont un ordre bien défini et une longueur finie. Ainsi, les affirmations sont plus faibles avecVector
, et cela conduit à une plus grande flexibilité.la source
case head +: tail
oucase tail :+ head
. Pour faire correspondre contre vide, vous pouvez fairecase Seq()
et ainsi de suite. Tout ce dont vous avez besoin est là dans l'API, qui est plus polyvalent queList
le sList
est implémenté avec une liste à liaison unique.Vector
est implémenté quelque chose comme JavaArrayList
.Eh bien, a
List
peut être incroyablement rapide si l'algorithme peut être implémenté uniquement avec::
,head
ettail
. J'ai eu une leçon d'objet très récemment, quand j'ai battu Javasplit
en générant unList
au lieu d'unArray
, et je ne pouvais pas battre ça avec autre chose.Cependant,
List
a un problème fondamental: il ne fonctionne pas avec des algorithmes parallèles. Je ne peux pas diviser unList
en plusieurs segments, ni le concaténer de manière efficace.Il existe d'autres types de collections qui peuvent mieux gérer le parallélisme - et
Vector
c'est l'un d'entre eux.Vector
a également une grande localité - ce quiList
n'est pas le cas - ce qui peut être un vrai plus pour certains algorithmes.Donc, tout bien considéré,
Vector
est le meilleur choix, sauf si vous avez des considérations spécifiques qui rendent l'une des autres collections préférable - par exemple, vous pouvez choisirStream
si vous voulez une évaluation et une mise en cache paresseuses (Iterator
est plus rapide mais ne met pas en cache), ouList
si l'algorithme est naturellement implémenté avec les opérations que j'ai mentionnées.Soit dit en passant, il est préférable d'utiliser
Seq
ou àIndexedSeq
moins que vous ne vouliez un morceau spécifique d'API (comme celuiList
de::
), ou mêmeGenSeq
ouGenIndexedSeq
si votre algorithme peut être exécuté en parallèle.la source
Vector
existe une structure de données immuable dans Scala?Certaines des déclarations ici sont déroutantes ou même fausses, en particulier l'idée qu'immuable.Vector dans Scala est quelque chose comme une ArrayList. La liste et le vecteur sont tous deux des structures de données immuables et persistantes (c'est-à-dire «bon marché pour obtenir une copie modifiée»). Il n'y a pas de choix par défaut raisonnable car il pourrait s'agir de structures de données mutables, mais cela dépend plutôt de ce que fait votre algorithme. List est une liste liée individuellement, tandis que Vector est un trie de base 32, c'est-à-dire qu'il s'agit d'une sorte d'arbre de recherche avec des nœuds de degré 32. En utilisant cette structure, Vector peut fournir les opérations les plus courantes raisonnablement rapidement, c'est-à-dire en O (log_32 ( n)). Cela fonctionne pour le préfixe, l'ajout, la mise à jour, l'accès aléatoire, la décomposition en tête / queue. L'itération dans un ordre séquentiel est linéaire. La liste, en revanche, fournit juste une itération linéaire et un pré-ajout à temps constant, une décomposition en tête / queue.
Cela pourrait ressembler à si Vector était un bon remplacement pour List dans presque tous les cas, mais le préfixe, la décomposition et l'itération sont souvent les opérations cruciales sur les séquences d'un programme fonctionnel, et les constantes de ces opérations sont (beaucoup) plus élevées pour le vecteur dû à sa structure plus compliquée. J'ai fait quelques mesures, donc l'itération est environ deux fois plus rapide pour la liste, le préfixe est environ 100 fois plus rapide sur les listes, la décomposition en tête / queue est environ 10 fois plus rapide sur les listes et la génération à partir d'un objet traversable est environ 2 fois plus rapide pour les vecteurs. (C'est probablement parce que Vector peut allouer des tableaux de 32 éléments à la fois lorsque vous le créez à l'aide d'un générateur au lieu d'ajouter ou d'ajouter des éléments un par un).
Alors, quelle structure de données devrions-nous utiliser? Fondamentalement, il existe quatre cas courants:
la source
Pour les collections immuables, si vous voulez une séquence, votre décision principale est d'utiliser
IndexedSeq
ou nonLinearSeq
, ce qui donne des garanties de performances différentes. Un IndexedSeq fournit un accès aléatoire rapide aux éléments et une opération de longueur rapide. Un LinearSeq fournit un accès rapide uniquement au premier élément viahead
, mais a également untail
fonctionnement rapide . (Tiré de la documentation Seq.)Pour un,
IndexedSeq
vous choisiriez normalement unVector
.Range
s etWrappedString
s sont également IndexedSeqs.Pour un,
LinearSeq
vous choisirez normalement unList
ou son équivalent paresseuxStream
. D'autres exemples sontQueue
s etStack
s.Donc, en termes Java,
ArrayList
utilisé de manière similaire à ScalaVector
, et deLinkedList
manière similaire à ScalaList
. Mais dans Scala, j'aurais tendance à utiliser List plus souvent que Vector, car Scala a un bien meilleur support pour les fonctions qui incluent la traversée de la séquence, comme le mappage, le pliage, l'itération, etc. Vous aurez tendance à utiliser ces fonctions pour manipuler la liste comme un plutôt que d'accéder de manière aléatoire à des éléments individuels.la source
Vector
'itération est plus rapide, mais quelqu'un doit le comparer pour en être sûr.Vector
existent physiquement ensemble sur la RAM en groupes de 32, qui s'intègrent plus pleinement dans le cache CPU ... donc il y a moins de manque de cacheDans les situations qui impliquent beaucoup d'accès aléatoire et de mutation aléatoire, un
Vector
(ou - comme le disent les docs - unSeq
) semble être un bon compromis. C'est également ce que suggèrent les caractéristiques de performance .De plus, la
Vector
classe semble bien jouer dans des environnements distribués sans beaucoup de duplication de données car il n'est pas nécessaire de faire une copie sur écriture pour l'objet complet. (Voir: http://akka.io/docs/akka/1.1.3/scala/stm.html#persistent-datastructures )la source
IndexedSeq
. Ce qui l'est aussiVector
, mais c'est une autre affaire.IndexedSeq
qui implémenteSeq
.Seq(1, 2, 3)
est unLinearSeq
qui est implémenté en utilisantList
.Si vous programmez immuablement et avez besoin d'un accès aléatoire, Seq est le chemin à parcourir (sauf si vous voulez un Set, ce que vous faites souvent). Sinon, List fonctionne bien, sauf que ses opérations ne peuvent pas être parallélisées.
Si vous n'avez pas besoin de structures de données immuables, restez avec ArrayBuffer car c'est l'équivalent Scala d'ArrayList.
la source