Il ne semble pas y avoir d'implémentation générique de OrderedDictionary
(qui se trouve dans l' System.Collections.Specialized
espace de noms) dans .NET 3.5. Y en a-t-il un qui me manque?
J'ai trouvé des implémentations pour fournir la fonctionnalité, mais je me suis demandé si / pourquoi il n'y avait pas d'implémentation générique prête à l'emploi et si quelqu'un sait si c'est quelque chose dans .NET 4.0?
OrderedDictionary<T>
: codeproject.com/Articles/18615/…Systems.Collections.Generic
. DemandonsOrderedDictionary<TKey,TValue>
.NET 5. Comme d'autres l'ont souligné, le cas où la clé est un int est dégénéré et nécessitera une attention particulière.Réponses:
Vous avez raison. Il n'y a pas d'équivalent générique
OrderedDictionary
dans le cadre lui-même.(C'est toujours le cas pour .NET 4 aussi, pour autant que je sache.)
Mais vous pouvez voter pour cela à UserVoice de Visual Studio (04/10/2016)!
la source
L'implémentation d'un générique
OrderedDictionary
n'est pas terriblement difficile, mais cela prend inutilement du temps et franchement cette classe est un énorme oubli de la part de Microsoft. Il existe plusieurs façons de l'implémenter, mais j'ai choisi d'utiliser unKeyedCollection
pour mon stockage interne. J'ai également choisi d'implémenter diverses méthodes pour trier comme cela,List<T>
car il s'agit essentiellement d'un IList et d'un IDictionary hybrides. J'ai inclus ma mise en œuvre ici pour la postérité.Voici l'interface. Notez qu'il inclut
System.Collections.Specialized.IOrderedDictionary
, qui est la version non générique de cette interface qui a été fournie par Microsoft.Voici l'implémentation avec les classes d'assistance:
Et aucune implémentation ne serait complète sans quelques tests (mais tragiquement, SO ne me laissera pas publier autant de code dans un seul post), donc je vais devoir vous laisser écrire vos tests. Mais, j'en ai laissé quelques-uns pour que vous puissiez avoir une idée de son fonctionnement:
-- METTRE À JOUR --
Source pour cela et d'autres bibliothèques .NET de base manquantes vraiment utiles ici: https://github.com/mattmc3/dotmore/blob/master/dotmore/Collections/Generic/OrderedDictionary.cs
la source
TKey
c'estint
? Commentthis[]
fonctionnera dans un tel cas?Pour mémoire, il existe un KeyedCollection générique qui permet aux objets d'être indexés par un int et une clé. La clé doit être intégrée dans la valeur.
la source
Voici une découverte bizarre: l'espace de noms System.Web.Util dans System.Web.Extensions.dll contient un OrderedDictionary générique
Je ne sais pas pourquoi MS l'a placé là au lieu du package System.Collections.Generic, mais je suppose que vous pouvez simplement copier-coller le code et l'utiliser (il est interne, vous ne pouvez donc pas l'utiliser directement). Il semble que l'implémentation utilise un dictionnaire standard et des listes de clés / valeurs séparées. Assez simple...
la source
System.Runtime.Collections
contient également un interneOrderedDictionary<TKey, TValue>
qui entoure juste la version non génériqueSystem.Web.Util.OrderedDictionary<TKey, TValue>
est implémenté en interne autour de genericDictionary
. Curieusement, il ne met pas en œuvreIList
maisICollection<KeyValuePair<TKey, TValue>>
List<KeyValuePair<TKey,TValue>>
sera compétitive en raison du modèle d'accès à la mémoire, pour les tailles plus grandes, utilisez simplement la même liste +Dictionary<TKey,int>
comme une recherche. AFAIK, il n'y a pas de telle structure de données qui fasse mieux en termes de vitesse / mémoire dans BigO.System.Collections.Specialized.OrderedDictionary
n'est pas un type générique. Regardez ma, pas de crochets à la page de documentation que vous avez liée: PPour ce que ça vaut, voici comment je l'ai résolu:
Il peut être initialisé comme ceci:
et accédé comme ceci:
la source
Add
méthodes.pairList.Add(new KeyValuePair<K,V>())
(c'est-à-dire la méthode sur laList
classe). Dans ce cas, leitsIndex
dictionnaire n'est pas mis à jour et maintenant la liste et le dictionnaire sont désynchronisés. Pourrait cacher laList.Add
méthode en créant unenew
PairList.Add(KeyValuePair<K,V>)
méthode, ou pourrait utiliser la composition au lieu de l'héritage et implémenter à nouveau toutes lesList
méthodes ... beaucoup plus de code ...if (itsindex.Contains(key)) return;
au début deAdd
pour éviter les doublonsUn problème conceptuel majeur avec une version générique de
OrderedDictionary
est que les utilisateurs de aOrderedDictionary<TKey,TValue>
s'attendent à pouvoir l'indexer soit numériquement à l'aide d'unint
, soit par recherche à l'aide d'unTKey
. Lorsque le seul type de clé étaitObject
, comme ce fut le cas avec non génériqueOrderedDictionary
, le type d'argument passé à l'indexeur serait suffisant pour distinguer si le type d'opération d'indexation doit être effectué. Dans l'état actuel des choses, cependant, on ne sait pas comment l'indexeur d'unOrderedDictionary<int, TValue>
doit se comporter.Si des classes comme
Drawing.Point
avaient recommandé et suivi une règle selon laquelle les structures mutables par morceaux devraient exposer leurs éléments mutables comme des champs plutôt que des propriétés, et s'abstenir d'utiliser des setters de propriété qui modifientthis
, alors unOrderedDictionary<TKey,TValue>
pourrait exposer efficacement uneByIndex
propriété qui retournait uneIndexer
structure qui contenait une référence à le dictionnaire, et avait une propriété indexée dont le getter et le setter feraient appelGetByIndex
etSetByIndex
sur lui. Ainsi, on pourrait dire quelque chose commeMyDict.ByIndex[5] += 3;
ajouter 3 au sixième élément du dictionnaire.Malheureusement, pour que le compilateur accepte une telle chose, il serait nécessaire de faire en sorte que la
ByIndex
propriété renvoie une nouvelle instance de classe plutôt qu'une structure à chaque fois qu'elle est invoquée, éliminant ainsi les avantages que l'on obtiendrait en évitant la boxe.Dans VB.NET, on pourrait contourner ce problème en utilisant une propriété indexée nommée (donc
MyDict.ByIndex[int]
serait membre deMyDict
, plutôt que d'exigerMyDict.ByIndex
d'être un membreMyDict
dont inclut un indexeur), mais C # ne permet pas de telles choses.Il aurait peut-être encore valu la peine d’offrir un
OrderedDictionary<TKey,TValue> where TKey:class
, mais la raison pour laquelle on a fourni des génériques en premier lieu était d’autoriser leur utilisation avec des types de valeur.la source
int
clés -typées présentent un défi, mais cela pourrait être évité en suivant l'exemple duSortedList<TKey, TValue>
type associé : ne supporte les clés qu'avec[...]
, et nécessite l'utilisation de.Values[...]
pour l'accès par index. (SortedDictionary<TKey, TValue>
, en revanche, qui n'est pas optimisé pour l'accès indexé, nécessite l'utilisation de.ElementAt(...).Value
)Pour de nombreuses raisons, j'ai trouvé que l'on pouvait s'en sortir avec un
List<KeyValuePair<K, V>>
. (Pas si vous en avez besoin pour étendreDictionary
, évidemment, et pas si vous avez besoin d'une recherche de valeur-clé meilleure que O (n).)la source
Tuple<T1, T2>
s'ils n'ont pas de relation clé-valeur.Il y a
SortedDictionary<TKey, TValue>
. Bien que sémantiquement proches, je ne prétends pas que c'est la même chose queOrderedDictionary
simplement parce qu'ils ne le sont pas. Même à partir des caractéristiques de performance. Cependant, la différence très intéressante et assez importante entreDictionary<TKey, TValue>
(et dans cette mesureOrderedDictionary
et les implémentations fournies dans les réponses) etSortedDictionary
est que ce dernier utilise l'arbre binaire en dessous. Il s'agit d'une distinction critique car elle rend la classe immunisée contre les contraintes de mémoire appliquées à la classe générique. Consultez ce thread à propos de laOutOfMemoryExceptions
levée lorsque la classe générique est utilisée pour gérer un grand ensemble de paires clé-valeur.Comment déterminer la valeur maximale du paramètre de capacité passé au constructeur de dictionnaire pour éviter OutOfMemoryException?
la source
SortedDictionary
dans l'ordre dans lequel elles ont été ajoutées ? C'est ce quiOrderedDictionary
permet. Concept différent de celui trié . (J'ai fait cette erreur dans le passé; je pensais que fournir un constructeur Comparer à OrderedDictionary le ferait trier, mais tout ce qu'il fait avec le Comparer est de déterminer l'égalité des clés; par exemple, le comparateur insensible aux chaînes permet la recherche de clés insensibles aux chaînes.)C'est vrai, c'est une omission malheureuse. OrderedDict de Python me manque
J'ai donc écrit ma propre
OrderedDictionary<K,V>
classe en C #. Comment ça marche? Il maintient deux collections - un dictionnaire non ordonné vanille et une liste ordonnée de clés. Avec cette solution, les opérations de dictionnaire standard conservent leur complexité rapide et la recherche par index est également rapide.https://gist.github.com/hickford/5137384
Voici l'interface
la source
Pour faire suite au commentaire de @VB, voici une implémentation accessible de System.Runtime.Collections.OrderedDictionary <,> . J'allais à l'origine y accéder par réflexion et le fournir via une usine mais la dll dans laquelle il se trouve ne semble pas du tout très accessible, alors j'ai juste extrait la source elle-même.
Une chose à noter est que l'indexeur ici ne lancera pas
KeyNotFoundException
. Je déteste absolument cette convention et c'est la 1 liberté que j'ai prise dans cette mise en œuvre. Si c'est important pour vous, remplacez simplement la ligne parreturn default(TValue);
. Utilise C # 6 ( compatible avec Visual Studio 2013 )Demandes d'extraction / discussion acceptées sur GitHub
la source
Pour ceux qui recherchent une option de package "officielle" dans NuGet, une implémentation d'un OrderedDictionary générique a été acceptée dans .NET CoreFX Lab. Si tout se passe bien, le type sera finalement approuvé et intégré au référentiel principal .NET CoreFX.
Il est possible que cette implémentation soit rejetée.
L'implémentation validée peut être référencée ici https://github.com/dotnet/corefxlab/blob/57be99a176421992e29009701a99a370983329a6/src/Microsoft.Experimental.Collections/Microsoft/Collections/Extensions/OrderedDictionary.cs
Le package NuGet qui a définitivement ce type disponible pour une utilisation peut être trouvé ici https://www.nuget.org/packages/Microsoft.Experimental.Collections/1.0.6-e190117-3
Ou vous pouvez installer le package dans Visual Studio. Recherchez le package "Microsoft.Experimental.Collections" et assurez-vous que la case "Inclure la version préliminaire" est cochée.
Mettra à jour ce message si et quand le type est rendu officiellement disponible.
la source
J'ai implémenté un générique
OrderedDictionary<TKey, TValue>
en enveloppantSortedList<TKey, TValue>
et en ajoutant un privateDictionary<TKey, int>
_order
. Ensuite, j'ai créé une implémentation interne deComparer<TKey>
, en passant une référence au dictionnaire _order. Ensuite, j'utilise ce comparateur pour la SortedList interne. Cette classe conserve l'ordre des éléments passés au constructeur et l'ordre des ajouts.Cette implémentation a presque les mêmes caractéristiques de gros O que
SortedList<TKey, TValue>
depuis que l'ajout et la suppression de _order sont O (1). Chaque élément prendra (selon le livre 'C # 4 in a Nutshell', p. 292, tableau 7-1) un espace mémoire supplémentaire de 22 (overhead) + 4 (int order) + TKey size (supposons 8) = 34 AvecSortedList<TKey, TValue>
la surcharge de deux octets, la surcharge totale est de 36 octets, tandis que le même livre dit que le non-génériqueOrderedDictionary
a une surcharge de 59 octets.Si je passe
sorted=true
au constructeur, alors _order n'est pas du tout utilisé, leOrderedDictionary<TKey, TValue>
est exactementSortedList<TKey, TValue>
avec une surcharge mineure pour l'encapsulation, voire pas du tout significative.Je vais stocker pas tellement de gros objets de référence dans le
OrderedDictionary<TKey, TValue>
, donc pour moi ça ca. Une surcharge de 36 octets est tolérable.Le code principal est ci-dessous. Le code mis à jour complet est sur cet essentiel .
la source
OrderedDictionary
, en ce qui concerne les insertions ou les suppressions: (1) Il n'y aura jamais de suppressions; (2) il y aura des suppressions, mais ce qui est important, c'est que les éléments sont énumérés dans l'ordre ajouté; il n'y a pas besoin d'accès par index; (3) l'index numérique d'un élément doit (ou au moins peut) rester constant, et pas plus de 2 milliards d'éléments ne seront ajoutés pendant la durée de vie de la collection, donc si l'élément n ° 7 est supprimé, il n'y aura plus jamais un article n ° 7; (4) l'index d'un item devrait être son rang par rapport aux survivants.Dictionary
. Les scénarios n ° 2 et n ° 3 pourraient être gérés en demandant à chaque élément de conserver un index indiquant quand il a été ajouté et des liens vers des éléments ajoutés avant ou après. Le scénario n ° 4 est le seul qui semble ne pas pouvoir atteindre les performances O (1) pour les opérations dans une séquence arbitraire. Selon les modèles d'utilisation, # 4 peut être aidé en utilisant diverses stratégies de mise à jour paresseuse (garder les décomptes dans une arborescence et faire invalider les modifications d'un nœud plutôt que de mettre à jour le nœud et ses parents).