web-dev-qa-db-fra.com

La meilleure façon de vérifier si une clé existe dans un dictionnaire avant de l'ajouter?

Lorsque vous obtenez une clé d'un dictionnaire dont vous n'êtes pas sûr, vous utilisez généralement TryGetValue au lieu de ContainsKey + l'indexeur get pour éviter la surcharge de vérification de la clé deux fois. En d'autres termes, ceci:

string password;
if (accounts.TryGetValue(username, out password))
{
    // use the password
}

serait préférable à ceci:

if (accounts.ContainsKey(username))
{
    string password = accounts[username];
}

Et si je voulais vérifier si une clé existait déjà avant de la définir sur une valeur? Par exemple, je voudrais vérifier si un nom d'utilisateur existait avant de le remplacer par un nouveau mot de passe:

if (!accounts.ContainsKey(username))
{
    accounts.Add(username, password);
}
else
{
    Console.WriteLine("Username is taken!");
}

contre

// this doesn't exist
if (!accounts.TrySetValue(username, password))
{
    Console.WriteLine("Username is taken!");
}

Existe-t-il une alternative plus performante à ContainsKey et Add qui fait cela?

10
James Ko

Si vous ne voulez pas remplacer, je pense qu'il est préférable d'écrire votre propre méthode d'extension comme TryGetValue. Il n'y a pas de méthode standard.

OR

Utilisez CuncurrentDictionary, il a la méthode TryAdd , mais vous aurez des frais généraux sur la synchronisation.

Donc, réponse simple - non, il n'y a pas une telle méthode.

5
Backs

Si vous pensez que l'insertion d'un nouveau nom sera le cas le plus courant et que la tentative d'insertion d'un doublon sera le cas rare, vous voudrez peut-être simplement utiliser la surcharge pour intercepter une exception.

try
{
    accounts.Add(username, password);
}
catch (ArgumentException)
{
    Console.WriteLine("Username is taken!");
}

Si vous appelez Add avec une clé existante, un ArgumentException sera lancé. Même si vous avez des doublons fréquents, cela sera probablement plus performant que votre contrôle ContainsKey.

4
Scott Chamberlain

J'ai tendance à écrire mes propres extensions au besoin.

Par exemple, GetValueOrDefault comme ceci:

public static V GetValueOrDefault<K, V>(this IDictionary<K, V> @this, K key, Func<V> @default)
{
    return @this.ContainsKey(key) ? @this[key] : @default();
}

Il peut être utilisé comme ceci:

var password = accounts.GetValueOrDefault(username, () => null);
if (password != null)
{
    //do stuff
}

Ou SetValueIfExists:

public static V SetValueIfExists<K, V>(this IDictionary<K, V> @this, K key, V value)
{
    if (@this.ContainsKey(key))
    {
        @this[key] = value;
    }
}

Ou SetValueIfNotExists:

public static V SetValueIfNotExists<K, V>(this IDictionary<K, V> @this, K key, V value)
{
    if ([email protected](key))
    {
        @this[key] = value;
    }
}
4
Enigmativity

Je sais que je suis en retard, mais vous pouvez utiliser une astuce et stocker le nombre avant l'ensemble d'indexeurs et vérifier le nombre après l'ensemble d'indexeurs. Si les nombres sont les mêmes, vous avez remplacé la clé, sinon vous avez ajouté un nouveau mappage:

public static bool AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> 
    dictionary, TKey key, TValue value)
{
    var countBefore = dictionary.Count;
    dictionary[key] = value;
    return countBefore != dictionary.Count;
}
2
MaximGurschi