Suite à ma confusion haletante , quelles sont les bonnes ressources qui expliquent comment le nouveau Scala 2.8 la bibliothèque des collections a été structurée. Je suis intéressé à trouver des informations sur la façon dont les éléments suivants s'assemblent:
List
, Iterable
)TraversableLike
)List.companion
)implicit
sont à portée à un moment donnéIl y a un 2.8 collection walk-through de Martin Odersky qui devrait probablement être votre première référence. Il a également été complété par notes architecturales , qui sera particulièrement intéressant pour ceux qui souhaitent concevoir leurs propres collections.
Le reste de cette réponse a été écrit bien avant qu'une telle chose n'existe (en fait, avant la version 2.8.0 elle-même).
Vous pouvez trouver un article à ce sujet sous la forme Scala SID # . D'autres articles dans ce domaine devraient également être intéressants pour les personnes intéressées par les différences entre Scala 2.7 et 2.8.
Je vais citer le document, de manière sélective, et compléter avec quelques réflexions. Il y a aussi quelques images, générées par Matthias sur decodified.com, et les fichiers SVG originaux peuvent être trouvés ici .
Il existe en fait trois hiérarchies de traits pour les collections: une pour les collections modifiables, une pour les collections immuables et une qui ne fait aucune hypothèse sur les collections.
Il existe également une distinction entre les collections parallèles, série et peut-être parallèles, introduite avec Scala 2.9. J'en parlerai dans la section suivante. La hiérarchie décrite dans cette section fait référence à exclusivement aux collections non parallèles.
L'image suivante montre la hiérarchie non spécifique introduite avec Scala 2.8:
Tous les éléments montrés sont des traits. Dans les deux autres hiérarchies, il existe également des classes héritant directement des traits ainsi que des classes qui peuvent être vues comme appartenant à cette hiérarchie par conversion implicite en classes wrapper. La légende de ces graphiques se trouve après eux.
Graphique pour une hiérarchie immuable:
Graphique pour la hiérarchie mutable:
Légende:
Voici une représentation abrégée ASCII de la hiérarchie de la collection, pour ceux qui ne peuvent pas voir les images.
Traversable
|
|
Iterable
|
+------------------+--------------------+
Map Set Seq
| | |
| +----+----+ +-----+------+
Sorted Map SortedSet BitSet Buffer Vector LinearSeq
Lorsque Scala 2.9 a introduit des collections parallèles, l'un des objectifs de conception était de rendre leur utilisation aussi transparente que possible. En termes plus simples, on peut remplacer une collection non parallèle (série) par une parallèle un, et en profiter instantanément.
Cependant, puisque toutes les collections étaient jusque-là en série, de nombreux algorithmes les utilisant supposaient et dépendaient du fait qu'elles l'étaient série. Les collections parallèles alimentant les méthodes avec de telles hypothèses échoueraient. Pour cette raison, toute la hiérarchie décrite dans la section précédente rend obligatoire le traitement en série.
Deux nouvelles hiérarchies ont été créées pour prendre en charge les collections parallèles.
La hiérarchie des collections parallèles porte les mêmes noms pour les traits, mais précédée de Par
: ParIterable
, ParSeq
, ParMap
et ParSet
. Notez qu'il n'y a pas de ParTraversable
, car toute collection prenant en charge l'accès parallèle est capable de prendre en charge le trait ParIterable
plus fort. Il n'a pas non plus certains des traits les plus spécialisés présents dans la hiérarchie sérielle. Toute cette hiérarchie se trouve dans le répertoire scala.collection.parallel
.
Les classes implémentant des collections parallèles diffèrent également, avec ParHashMap
et ParHashSet
pour les collections parallèles mutables et immuables, plus ParRange
et ParVector
implémentant immutable.ParSeq
et ParArray
implémentant mutable.ParSeq
.
Il existe également une autre hiérarchie qui reflète les traits des collections série et parallèle, mais avec un préfixe Gen
: GenTraversable
, GenIterable
, GenSeq
, GenMap
et GenSet
. Ces traits sont parents pour les collections parallèles et en série. Cela signifie qu'une méthode prenant un Seq
ne peut pas recevoir une collection parallèle, mais qu'une méthode prenant un GenSeq
devrait fonctionner avec des collections en série et parallèles.
Étant donné la structure de ces hiérarchies, le code écrit pour Scala 2.8 était entièrement compatible avec Scala 2.9, et exigeait un comportement en série. Sans être réécrit, il ne peut pas prendre avantage des collections parallèles, mais les changements requis sont très faibles.
Toute collection peut être convertie en collection parallèle en appelant la méthode par
dessus. De même, toute collection peut être convertie en collection en appelant la méthode seq
dessus.
Si la collecte était déjà du type demandé (parallèle ou série), aucune conversion n'aura lieu. Cependant, si l'on appelle seq
sur une collection parallèle ou par
sur une collection série, une nouvelle collection avec la caractéristique demandée sera générée.
Ne confondez pas seq
, qui transforme une collection en une collection non parallèle, avec toSeq
, qui renvoie un Seq
créé à partir des éléments de la collection. L'appel de toSeq
sur une collection parallèle renverra un ParSeq
, pas une collection série.
Bien qu'il existe de nombreuses classes d'implémentation et sous-portraits, il existe certains traits de base dans la hiérarchie, chacun fournissant plus de méthodes ou des garanties plus spécifiques, mais réduisant le nombre de classes qui pourraient les implémenter.
Dans les sous-sections suivantes, je donnerai une brève description des principaux traits et de l'idée qui les sous-tend.
Ce trait est à peu près comme le trait Traversable
décrit ci-dessous, mais avec la limitation que vous ne pouvez l'utiliser que une fois. Autrement dit, toutes les méthodes appelées à un TraversableOnce
mai le rendent inutilisable.
Cette limitation permet de partager les mêmes méthodes entre les collections et Iterator
. Cela permet à une méthode qui fonctionne avec un Iterator
mais qui n'utilise pas de méthodes spécifiques à Iterator
de pouvoir réellement travailler avec n'importe quelle collection, plus les itérateurs, si elle est réécrite pour accepter TraversableOnce
.
Parce que TraversableOnce
unifie les collections et les itérateurs, il n'apparaît pas dans les graphiques précédents, qui ne concernent que les collections.
Au sommet de la hiérarchie se trouve le trait Traversable
. Sa seule opération abstraite est
def foreach[U](f: Elem => U)
L'opération est destinée à parcourir tous les éléments de la collection et à appliquer l'opération donnée f à chaque élément. L'application est effectuée uniquement pour son effet secondaire; en fait, tout résultat de fonction de f est rejeté par foreach.
Les objets transversaux peuvent être finis ou infinis. Un exemple d'objet traversable infini est le flux de nombres naturels Stream.from(0)
. La méthode hasDefiniteSize
indique si une collection est éventuellement infinie. Si hasDefiniteSize
renvoie true, la collection est certainement finie. Si elle retourne false, la collection n'est pas encore complètement élaborée, elle peut donc être infinie ou finie.
Cette classe définit des méthodes qui peuvent être implémentées efficacement en termes de foreach
(plus de 40 d'entre elles).
Ce trait déclare une méthode abstraite iterator
qui renvoie un itérateur qui produit tous les éléments de la collection un par un. La méthode foreach
dans Iterable
est implémentée en termes de iterator
. Les sous-classes de Iterable
remplacent souvent foreach avec une implémentation directe pour plus d'efficacité.
La classe Iterable
ajoute également des méthodes moins utilisées à Traversable
, qui ne peuvent être mises en œuvre efficacement que si un iterator
est disponible. Ils sont résumés ci-dessous.
xs.iterator An iterator that yields every element in xs, in the same order as foreach traverses elements.
xs takeRight n A collection consisting of the last n elements of xs (or, some arbitrary n elements, if no order is defined).
xs dropRight n The rest of the collection except xs takeRight n.
xs sameElements ys A test whether xs and ys contain the same elements in the same order
Après Iterable
, trois traits de base en héritent: Seq
, Set
et Map
. Tous les trois ont une méthode apply
et tous les trois implémentent le trait PartialFunction
, mais la signification de apply
est différente dans chaque cas.
J'espère que la signification de Seq
, Set
et Map
est intuitive. Après eux, les classes se décomposent en implémentations spécifiques qui offrent des garanties particulières en termes de performances, et les méthodes qu'elle met à disposition en conséquence. Quelques traits supplémentaires sont également disponibles, tels que LinearSeq
, IndexedSeq
et SortedSet
.
La liste ci-dessous peut être améliorée. Laissez un commentaire avec des suggestions et je le corrigerai.
Traversable
- Classe de collection de base. Peut être implémenté uniquement avec foreach
. TraversableProxy
- Proxy pour un Traversable
. Pointez simplement self
vers la vraie collection.TraversableView
- Traversable avec quelques méthodes non strictes.TraversableForwarder
- Transmet la plupart des méthodes à underlying
, sauf toString
, hashCode
, equals
, stringPrefix
, newBuilder
, view
et tous les appels créant un nouvel objet itérable du même type.mutable.Traversable
Et immutable.Traversable
- même chose que Traversable
, mais en restreignant le type de collection.Iterable
, telles que MetaData
, existent.Iterable
- Une collection pour laquelle un Iterator
peut être créé (via iterator
). IterableProxy
, IterableView
, mutable
et immutable.Iterable
.Iterator
- Un trait qui n'est pas descendant de Traversable
. Définissez next
et hasNext
. CountedIterator
- Un Iterator
définissant count
, qui renvoie les éléments vus jusqu'ici.BufferedIterator
- Définit head
, qui renvoie l'élément suivant sans le consommer.Iterator
, telles que Source
, existent.Map
- Un Iterable
de Tuple2
, Qui fournit également des méthodes pour récupérer une valeur (le deuxième élément du tuple) à partir d'une clé (le premier élément du tuple) . Étend également PartialFunction
. MapProxy
- Un Proxy
pour un Map
.DefaultMap
- Un trait implémentant certaines des méthodes abstraites de Map
.SortedMap
- Un Map
dont les clés sont triées. immutable.SortMap
immutable.TreeMap
- Une classe implémentant immutable.SortedMap
.immutable.Map
immutable.MapProxy
immutable.HashMap
- Une classe implémentant immutable.Map
Via le hachage de clé.immutable.IntMap
- Une classe implémentant immutable.Map
Spécialisée pour les clés Int
. Utilise un arbre basé sur les chiffres binaires des clés.immutable.ListMap
- Une classe implémentant immutable.Map
À travers des listes.immutable.LongMap
- Une classe implémentant immutable.Map
Spécialisée pour les clés Long
. Voir IntMap
.mutable.Map
mutable.HashMap
- Une classe implémentant mutable.Map
Via le hachage de clé.mutable.ImmutableMapAdaptor
- Une classe implémentant un mutable.Map
À partir d'un immutable.Map
Existant.mutable.LinkedHashMap
-?mutable.ListMap
- Une classe implémentant mutable.Map
À travers des listes.mutable.MultiMap
- Une classe acceptant plus d'une valeur distincte pour chaque clé.mutable.ObservableMap
- A mixin qui, lorsqu'il est mélangé avec un Map
, publie des événements aux observateurs via une interface Publisher
.mutable.OpenHashMap
- Une classe basée sur un algorithme de hachage ouvert.mutable.SynchronizedMap
- A mixin qui doit être mélangé avec un Map
pour en fournir une version avec des méthodes synchronisées.mutable.MapProxy
.Seq
- Une séquence d'éléments. On suppose une taille et une répétition d'éléments bien définies. Étend également PartialFunction
. IndexedSeq
- Séquences prenant en charge O(1) accès aux éléments et O(1) calcul de longueur. IndexedSeqView
immutable.PagedSeq
- Une implémentation de IndexedSeq
où les éléments sont produits à la demande par une fonction passée par le constructeur.immutable.IndexedSeq
immutable.Range
- Une séquence délimitée d'entiers, fermée à l'extrémité inférieure, ouverte à l'extrémité supérieure et avec une étape. immutable.Range.Inclusive
- Un Range
a également fermé en haut de gamme.immutable.Range.ByOne
- Un Range
dont l'étape est 1.immutable.NumericRange
- Une version plus générique de Range
qui fonctionne avec tout Integral
. immutable.NumericRange.Inclusive
, immutable.NumericRange.Exclusive
.immutable.WrappedString
, immutable.RichString
- Wrappers qui permet de voir un String
comme un Seq[Char]
, Tout en conservant les méthodes String
. Je ne sais pas quelle est la différence entre eux.mutable.IndexedSeq
mutable.GenericArray
- Une structure de type tableau basée sur Seq
. Notez que la "classe" Array
est la Array
de Java, qui est plus une méthode de stockage en mémoire qu'une classe.mutable.ResizableArray
- Classe interne utilisée par les classes basées sur des tableaux redimensionnables.mutable.PriorityQueue
, mutable.SynchronizedPriorityQueue
- Classes implémentant des files d'attente priorisées - files d'attente où les éléments sont retirés de la file d'attente selon un Ordering
en premier, et l'ordre de mise en file d'attente en dernier.mutable.PriorityQueueProxy
- un résumé Proxy
pour un PriorityQueue
.LinearSeq
- Un trait pour les séquences linéaires, avec un temps efficace pour isEmpty
, head
et tail
. immutable.LinearSeq
immutable.List
- Une implémentation de liste immuable, à lien unique.immutable.Stream
- Une liste paresseuse. Ses éléments ne sont calculés qu'à la demande, mais mémorisés (conservés en mémoire) par la suite. Il peut être théoriquement infini.mutable.LinearSeq
mutable.DoublyLinkedList
- Une liste avec prev
, head
(elem
) et tail
(next
) mutables.mutable.LinkedList
- Une liste avec head
(elem
) et tail
(next
) mutables.mutable.MutableList
- Une classe utilisée en interne pour implémenter des classes basées sur des listes mutables. mutable.Queue
, mutable.QueueProxy
- Une structure de données optimisée pour les opérations FIFO (Premier entré, premier sorti).mutable.QueueProxy
- Un Proxy
pour un mutable.Queue
.SeqProxy
, SeqView
, SeqForwarder
immutable.Seq
immutable.Queue
- Une classe implémentant une structure de données optimisée FIFO (First-In, First-Out). Il n'y a pas de superclasse commune des files d'attente mutable
et immutable
.immutable.Stack
- Une classe implémentant une structure de données optimisée par LIFO (Last-In, First-Out). Il n'y a pas de superclasse commune des deux piles mutable
immutable
.immutable.Vector
-?scala.xml.NodeSeq
- Une classe XML spécialisée qui étend immutable.Seq
.immutable.IndexedSeq
- Comme vu ci-dessus.immutable.LinearSeq
- Comme vu ci-dessus.mutable.ArrayStack
- Une classe implémentant une structure de données optimisée pour LIFO à l'aide de tableaux. Soi-disant nettement plus rapide qu'une pile normale.mutable.Stack
, mutable.SynchronizedStack
- Classes implémentant une structure de données optimisée pour LIFO.mutable.StackProxy
- Un Proxy
pour un mutable.Stack
..mutable.Seq
mutable.Buffer
- Séquence d'éléments pouvant être modifiés en ajoutant, en ajoutant ou en ajoutant de nouveaux membres. mutable.ArrayBuffer
- Une implémentation de la classe mutable.Buffer
, Avec un temps amorti constant pour les opérations d'ajout, de mise à jour et d'accès aléatoire. Il a quelques sous-classes spécialisées, telles que NodeBuffer
.mutable.BufferProxy
, mutable.SynchronizedBuffer
.mutable.ListBuffer
- Un tampon soutenu par une liste. Il fournit un ajout et un ajout de temps constant, la plupart des autres opérations étant linéaires.mutable.ObservableBuffer
- A mixin trait qui, lorsqu'il est mélangé à un Buffer
, fournit des événements de notification via une interface Publisher
.mutable.IndexedSeq
- Comme vu ci-dessus.mutable.LinearSeq
- Comme vu ci-dessus.Set
- Un ensemble est une collection qui comprend au plus un objet quelconque. BitSet
- Un ensemble d'entiers stockés sous forme de jeu de bits. immutable.BitSet
mutable.BitSet
SortedSet
- Un ensemble dont les éléments sont ordonnés. immutable.SortedSet
immutable.TreeSet
- Une implémentation d'un SortedSet
basée sur un arbre.SetProxy
- Un Proxy
pour un Set
.immutable.Set
immutable.HashSet
- Une implémentation de Set
basée sur le hachage des éléments.immutable.ListSet
- Une implémentation de Set
basée sur des listes.immutable.SetProxy
- Un Proxy
pour un Set
immuable.mutable.Set
mutable.HashSet
- Une implémentation de Set
basée sur le hachage des éléments.mutable.ImmutableSetAdaptor
- Une classe implémentant un Set
mutable à partir d'un Set
immuable.LinkedHashSet
- Une implémentation de Set
basée sur des listes.ObservableSet
- A mixin trait qui, lorsqu'il est mélangé avec un Set
, fournit des événements de notification via une interface Publisher
.SetProxy
- Un Proxy
pour un Set
.SynchronizedSet
- A mixin trait qui, lorsqu'il est mélangé avec un Set
, fournit des événements de notification via une interface Publisher
.Cela a été fait pour obtenir une réutilisation maximale du code. L'implémentation concrète générique pour les classes avec une certaine structure (un traversable, une carte, etc.) se fait dans les classes Like. Les classes destinées à la consommation générale remplacent alors les méthodes sélectionnées qui peuvent être optimisées.
Le générateur pour les classes, c'est-à-dire l'objet qui sait comment créer des instances de cette classe d'une manière qui peut être utilisée par des méthodes comme map
, est créé par une méthode dans l'objet compagnon. Donc, pour construire un objet de type X, je dois obtenir ce générateur de l'objet compagnon de X. Malheureusement, il n'y a aucun moyen, dans Scala, de passer de la classe X à l'objet X. À cause de cela, il y a une méthode définie dans chaque instance de X, companion
, qui renvoie l'objet compagnon de la classe X.
Bien qu'une telle méthode puisse être utilisée dans des programmes normaux, sa cible est d'activer la réutilisation de code dans la bibliothèque de collection.
Vous n'êtes pas censé vous en soucier. Ils sont implicites précisément pour que vous n'ayez pas besoin de comprendre comment le faire fonctionner.
Ces implications existent pour permettre aux méthodes sur les collections d'être définies sur les classes parentes mais retournent toujours une collection du même type. Par exemple, la méthode map
est définie sur TraversableLike
, mais si vous l'utilisez sur un List
, vous obtiendrez un List
.