web-dev-qa-db-fra.com

Pourquoi est-il plus rapide de vérifier si le dictionnaire contient la clé plutôt que d'attraper l'exception au cas où ce ne serait pas le cas?

Imaginez le code:

public class obj
{
    // elided
}

public static Dictionary<string, obj> dict = new Dictionary<string, obj>();

Méthode 1

public static obj FromDict1(string name)
{
    if (dict.ContainsKey(name))
    {
        return dict[name];
    }
    return null;
}

Méthode 2

public static obj FromDict2(string name)
{
    try
    {
        return dict[name];
    }
    catch (KeyNotFoundException)
    {
        return null;
    }
}

J'étais curieux de savoir s'il y avait une différence dans les performances de ces 2 fonctions, car la première DEVRAIT ÊTRE PLUS LENTE que la seconde - étant donné qu'elle doit vérifier deux fois si le dictionnaire contient une valeur, alors que la deuxième fonction n'a besoin que d'accéder au dictionnaire. une fois mais WOW, c'est en fait le contraire:

Boucle pour 1 000 000 valeurs (avec 100 000 existantes et 900 000 non existantes):

première fonction: 306 millisecondes

seconde fonction: 20483 millisecondes

Pourquoi donc?

EDIT: Comme vous pouvez le constater dans les commentaires ci-dessous, les performances de la deuxième fonction sont en réalité légèrement meilleures que celles de la première au cas où il y aurait 0 clés non existantes. Mais lorsqu'il y a au moins une ou plusieurs clés non existantes, les performances de la seconde diminuent rapidement.

234
Petr

D'une part, lancer des exceptions est par nature coûteux , car la pile doit être déroulée, etc.
D'autre part, accéder à une valeur dans un dictionnaire par sa clé est peu coûteux, car c'est une opération rapide, O(1).

BTW: La bonne façon de faire est d'utiliser TryGetValue

obj item;
if(!dict.TryGetValue(name, out item))
    return null;
return item;

Cela accède au dictionnaire une seule fois au lieu de deux fois.
Si vous voulez vraiment renvoyer null si la clé n’existe pas, le code ci-dessus peut être simplifié:

obj item;
dict.TryGetValue(name, out item);
return item;

Cela fonctionne car TryGetValue définit item sur null si aucune clé avec name n'existe.

401
Daniel Hilgarth

Les dictionnaires sont spécialement conçus pour effectuer des recherches de clés très rapides. Elles sont implémentées sous forme de tables de hachage et plus le nombre d'entrées est élevé, plus elles sont rapides par rapport aux autres méthodes. L'utilisation du moteur d'exceptions n'est supposée être effectuée que lorsque votre méthode a échoué, comme il a été conçu, car il s'agit d'un ensemble d'objets volumineux qui vous offre de nombreuses fonctionnalités pour la gestion des erreurs. J'ai construit une classe de bibliothèque entière une fois avec tout ce qui était entouré de blocs catch try une fois et j'étais consterné de voir la sortie de débogage qui contenait une ligne distincte pour chacune des plus de 600 exceptions!

6
Ed Hermanson