J'essaie de comprendre quelles sont les structures de données les plus efficaces et quand/où les utiliser.
Maintenant, il se peut que je ne comprenne tout simplement pas assez les structures, mais en quoi une ILookup(of key, ...)
est-elle différente d'un Dictionary(of key, list(of ...))
?
Aussi, où voudrais-je utiliser un ILookup
et où serait-il plus efficace en termes de vitesse du programme/accès mémoire/données, etc.?
Deux différences significatives:
Lookup
est immuable. Yay :) (Du moins, je pense que la classe concrète Lookup
est immuable, et que l'interface ILookup
ne fournit aucun membre en mutation. Là pourrait soit d'autres implémentations mutables, bien sûr.)KeyNotFoundException
. (Il n'y a donc pas de TryGetValue
, AFAICR.)Ils sont susceptibles d’être équivalents en efficacité - la recherche peut très bien utiliser un Dictionary<TKey, GroupingImplementation<TValue>>
En coulisse, par exemple. Choisissez entre eux en fonction de vos besoins. Personnellement, je trouve que la recherche est généralement un meilleur ajustement qu'un Dictionary<TKey, List<TValue>>
, Principalement en raison des deux premiers points ci-dessus.
Notez qu'en tant que détail d'implémentation, l'implémentation concrète de IGrouping<,>
Utilisée pour les valeurs implémente IList<TValue>
, Ce qui signifie qu'il est efficace de l'utiliser avec Count()
, ElementAt()
etc.
Il est intéressant de noter que personne n’a indiqué la plus grande différence réelle (directement à partir de MSDN ):
Une recherche ressemble à un dictionnaire. La différence est qu'un dictionnaire mappe des clés à des valeurs uniques, tandis qu'une recherche mappe des clés à des collections de valeurs.
Un Dictionary<Key, List<Value>>
Et un Lookup<Key, Value>
Peuvent logiquement contenir des données organisées de la même manière et sont tous deux du même ordre d'efficacité. La principale différence est que Lookup
est immuable: il n’a pas de méthode Add()
ni de constructeur public (et comme Jon l’a mentionné, vous pouvez interroger une clé non existante sans exception et avoir la clé as partie du groupement).
Pour ce qui vous utilisez, cela dépend vraiment de la façon dont vous voulez les utiliser. Si vous conservez une mappe de clé pour plusieurs valeurs constamment modifiée, un Dictionary<Key, List<Value>>
Est probablement préférable car il est modifiable.
Si, toutefois, vous avez une séquence de données et souhaitez simplement obtenir une vue en lecture seule des données organisée par clé, une recherche est très facile à construire et vous donnera un instantané en lecture seule.
La principale différence entre un ILookup<K,V>
et un Dictionary<K, List<V>>
est qu'un dictionnaire est modifiable; vous pouvez ajouter ou supprimer des clés, mais aussi ajouter ou supprimer des éléments de la liste qui est recherchée. Un ILookup
est immuable et ne peut pas être modifié une fois créé.
L'implémentation sous-jacente des deux mécanismes sera identique ou similaire, de sorte que leur vitesse de recherche et leur empreinte mémoire seront approximativement les mêmes.
Une autre différence qui n’a pas encore été mentionnée est que Lookup () prend en charge les clés nulles :
La classe Lookup implémente l'interface ILookup. Lookup est très similaire à un dictionnaire, sauf que plusieurs valeurs peuvent être mappées sur la même clé et que les clés nulles sont prises en charge.
Si vous essayez d'obtenir une structure aussi efficace qu'un Dictionary
mais que vous ne savez pas avec certitude qu'il n'y a pas de clé dupliquée en entrée, Lookup
est plus sûr.
Comme indiqué dans une autre réponse, il prend également en charge les clés nulles et renvoie toujours un résultat valide lorsqu'il est interrogé avec des données arbitraires. Par conséquent, il semble plus résistant aux entrées inconnues (moins enclin que Dictionary à générer des exceptions).
Et c'est particulièrement vrai si vous le comparez à la System.Linq.Enumerable.ToDictionary
une fonction :
// won't throw
new[] { 1, 1 }.ToLookup(x => x);
// System.ArgumentException: An item with the same key has already been added.
new[] { 1, 1 }.ToDictionary(x => x);
L'alternative serait d'écrire votre propre code de gestion de clé dupliqué à l'intérieur d'une boucle foreach
.
Si vous n'avez pas besoin d'une liste et que vous allez gérer un grand nombre d'éléments, Dictionary
(ou même votre propre structure personnalisée) serait plus efficace:
Stopwatch stopwatch = new Stopwatch();
var list = new List<string>();
for (int i = 0; i < 5000000; ++i)
{
list.Add(i.ToString());
}
stopwatch.Start();
var lookup = list.ToLookup(x => x);
stopwatch.Stop();
Console.WriteLine("Creation: " + stopwatch.Elapsed);
// ... Same but for ToDictionary
var lookup = list.ToDictionary(x => x);
// ...
Comme Lookup
doit maintenir une liste d'éléments pour chaque clé, il est plus lent que Dictionary (environ 3 fois plus lent pour un très grand nombre d'éléments)
Recherche dans la vitesse: Création: 00: 00: 01.5760444
Vitesse du dictionnaire: Création: 00: 00: 00.4418833