De l'entrée de MSDN sur la méthode Dictionary.TryGetValue :
Cette méthode combine les fonctionnalités de la méthode ContainsKey et la propriété Item.
Si la clé n'est pas trouvée, le paramètre de valeur obtient la valeur par défaut appropriée pour le type de valeur TValue; par exemple, 0 (zéro) pour les types entiers, false pour les types booléens et null pour les types de référence.
Utilisez la méthode TryGetValue si votre code tente fréquemment d'accéder à des clés qui ne figurent pas dans le dictionnaire. L'utilisation de cette méthode est plus efficace que la capture de l'exception KeyNotFoundException levée par la propriété Item.
Cette méthode approche une opération O (1).
D'après la description, il n'est pas clair si c'est plus efficace ou tout simplement plus pratique que d'appeler ContainsKey et de faire la recherche. La mise en œuvre deTryGetValue
simplement appeler ContainsKey puis Item ou est en fait plus efficace que cela en faisant une seule recherche?
En d'autres termes, ce qui est plus efficace (c'est-à-dire lequel effectue le moins de recherches):
Dictionary<int,int> dict;
//...//
int ival;
if(dict.ContainsKey(ikey))
{
ival = dict[ikey];
}
else
{
ival = default(int);
}
ou
Dictionary<int,int> dict;
//...//
int ival;
dict.TryGetValue(ikey, out ival);
Remarque: je ne recherche pas de référence!
la source
if(dict.ContainsKey(ikey)) dict[ikey]++; else dict.Add(ikey, 0);
. Mais je pense queTryGetValue
c'est encore plus efficace puisque le get et le set de la propriété indexeur sont utilisés, n'est-ce pas?Un benchmark rapide montre qui
TryGetValue
a un léger avantage:Cela produit
rendant l'
ContainsKey + Item
accès environ 40% plus lent en supposant un mélange homogène de coups sûrs et manqués.De plus, lorsque je change le programme pour toujours manquer (c'est-à-dire toujours en levant les yeux
"b"
), les deux versions deviennent tout aussi rapides:Quand je le fais "tous les hits", cependant, le
TryGetValue
reste clairement gagnant:la source
TryGetValue
devriez être loin devant. De plus ... un coup de pinceau ...DateTime
n'est pas le meilleur moyen de capturer des mesures de performances.TryGetValue
placez encore plus en tête. J'ai modifié la réponse pour inclure les scénarios "tous les succès" et "tous les échecs".Any
- comme ceci:Any(i=>i.Key==key)
. Dans ce cas, oui, c'est une mauvaise recherche linéaire du dictionnaire.DateTime.Now
ne sera précis qu'à quelques ms. Utilisez plutôt laStopwatch
classe inSystem.Diagnostics
(qui utilise QueryPerformanceCounter sous les couvertures pour fournir une précision beaucoup plus élevée). Il est également plus facile à utiliser.Puisqu'aucune des réponses jusqu'à présent ne répond à la question, voici une réponse acceptable que j'ai trouvée après quelques recherches:
Si vous décompilez TryGetValue, vous voyez que c'est le cas:
tandis que la méthode ContainsKey est:
donc TryGetValue est juste ContainsKey plus une recherche de tableau si l'élément est présent.
La source
Il semble que TryGetValue sera presque deux fois plus rapide que la combinaison ContainsKey + Item.
la source
On s'en fout :-)
Vous demandez probablement parce que
TryGetValue
c'est pénible à utiliser - alors encapsulez-le comme ceci avec une méthode d'extension.Ensuite, appelez simplement:
ou
la source
this
mais il s'avère que j'ai ma méthode dupliquée deux fois dans ma base de code - une fois avec et une sans lethis
c'est pourquoi je ne l'ai jamais attrapé! merci pour la réparation!TryGetValue
attribue une valeur par défaut au paramètre de valeur de sortie si la clé n'existe pas, cela pourrait donc être simplifié.if(!dic.TryGetValue(key, out value item)) item = dic[key] = new Item();
Pourquoi ne le testes-tu pas?
Mais je suis sûr que
TryGetValue
c'est plus rapide, car il ne fait qu'une seule recherche. Bien sûr, cela n'est pas garanti, c'est-à-dire que différentes implémentations peuvent avoir des caractéristiques de performances différentes.La façon dont j'implémenterais un dictionnaire consiste à créer une
Find
fonction interne qui trouve l'emplacement pour un élément, puis à créer le reste par-dessus.la source
Jusqu'à présent, toutes les réponses, bien que bonnes, manquent un point essentiel.
Les méthodes dans les classes d'une API (par exemple le framework .NET) font partie d'une définition d'interface (pas une interface C # ou VB, mais une interface au sens informatique).
En tant que tel, il est généralement incorrect de demander si l'appel d'une telle méthode est plus rapide, à moins que la vitesse ne fasse partie de la définition d'interface formelle (ce qui n'est pas le cas dans ce cas).
Traditionnellement, ce type de raccourci (combinant recherche et récupération) est plus efficace quels que soient le langage, l'infrastructure, le système d'exploitation, la plate-forme ou l'architecture de la machine. Il est également plus lisible, car il exprime votre intention de manière explicite, plutôt que de l'impliquer (à partir de la structure de votre code).
Donc, la réponse (d'un vieux hack grisonnant) est définitivement «Oui» (TryGetValue est préférable à une combinaison de ContainsKey et Item [Get] pour récupérer une valeur à partir d'un dictionnaire).
Si vous pensez que cela semble étrange, pensez-y comme ceci: même si les implémentations actuelles de TryGetValue, ContainsKey et Item [Get] ne produisent aucune différence de vitesse, vous pouvez supposer qu'il est probable qu'une implémentation future (par exemple .NET v5) fera (TryGetValue sera plus rapide). Pensez à la durée de vie de votre logiciel.
Soit dit en passant, il est intéressant de noter que les technologies de définition d'interface modernes typiques fournissent encore rarement un moyen de définir formellement les contraintes de synchronisation. Peut-être .NET v5?
la source
En faisant un programme de test rapide, il y a certainement une amélioration en utilisant TryGetValue avec 1 million d'éléments dans un dictionnaire.
Résultats:
Contient une clé + un élément pour 1000000 hits: 45 ms
TryGetValue pour 1000000 hits: 26ms
Voici l'application de test:
la source
Sur ma machine, avec beaucoup de RAM, lorsqu'elle est exécutée en mode RELEASE (pas DEBUG),
ContainsKey
est égal àTryGetValue
/try-catch
si toutes les entrées duDictionary<>
sont trouvées.ContainsKey
les surpasse tous de loin quand il n'y a que quelques entrées de dictionnaire introuvables (dans mon exemple ci-dessous, régléMAXVAL
sur quelque chose de plus grand queENTRIES
d'avoir certaines entrées manquées):Résultats:
Voici mon code:
la source
Mis à part la conception d'une microbenchmark qui donnera des résultats précis dans un cadre pratique, vous pouvez inspecter la source de référence de .NET Framework.
System.Collections.Generic.Dictionary<TKey, TValue>.TryGetValue(TKey, out TValue)
System.Collections.Generic.Dictionary<TKey, TValue>.ContainsKey(TKey)
System.Collections.Generic.Dictionary<TKey, TValue>.Item(TKey)
Tous appellent la
FindEntry(TKey)
méthode qui fait la plupart du travail et ne mémorise pas son résultat, donc appelerTryGetValue
est presque deux fois plus rapide queContainsKey
+Item
.L'interface peu pratique de
TryGetValue
peut être adaptée à l'aide d'une méthode d'extension :Depuis C # 7.1, vous pouvez remplacer
default(TValue)
par plaindefault
. Le type est déduit.Usage:
Il renvoie
null
pour les types de référence dont la recherche échoue, sauf si une valeur par défaut explicite est spécifiée.la source
Si vous essayez d'extraire la valeur du dictionnaire, TryGetValue (clé, valeur de sortie) est la meilleure option, mais si vous vérifiez la présence de la clé, pour une nouvelle insertion, sans écraser les anciennes clés, et seulement avec cette portée, ContainsKey (clé) est la meilleure option, le benchmark peut le confirmer:
Ceci est un véritable exemple, j'ai un service qui pour chaque "article" créé, il associe un numéro progressif, ce numéro, chaque fois que vous créez un nouvel article, doit être trouvé gratuit, si vous supprimez un article, le numéro gratuit devient gratuit, bien sûr, ce n'est pas optimisé, car j'ai un var statique qui met en cache le numéro actuel, mais si vous terminez tous les nombres, vous pouvez recommencer de 0 à UInt32.MaxValue
Test exécuté:
Ajout d'éléments dans la table de hachage ...
Fait en 0,5908 - pause ....
Ajout d'éléments dans le dictionnaire ...
Fait en 0,2679 - pause ....
Recherche du premier numéro libre pour l'insertion
Première méthode : ContainsKey
Fait en 0,0561 - valeur ajoutée 1000000 dans le dictionnaire - pause ....
Deuxième méthode: TryGetValue
Fait en 0,0643 - valeur ajoutée 1000001 dans le dictionnaire - pause ....
Test de la table de hachage
Terminé en 0, 3015 - valeur ajoutée 1000000 dans la table de hachage - pause ....
Appuyez sur n'importe quelle touche pour continuer. .
Si certains d'entre vous demandent si les ContainsKeys pourraient avoir un avantage, j'ai même essayé d'inverser la clé TryGetValue avec Contains, le résultat est le même.
Donc, pour moi, avec une dernière considération, tout dépend de la façon dont le programme se comporte.
la source