web-dev-qa-db-fra.com

Dois-je utiliser un dictionnaire C # si je n'ai besoin que d'une recherche rapide de clés et que les valeurs ne sont pas pertinentes?

J'ai besoin d'un type de données capable d'insérer des entrées et de déterminer rapidement si une entrée a déjà été insérée. Un Dictionary semble répondre à ce besoin (voir exemple). Cependant, je n'ai aucune utilité pour le values du dictionnaire. Dois-je toujours utiliser un dictionnaire ou existe-t-il un autre type de données mieux adapté?

public class Foo
{
    private Dictionary<string, bool> Entities;

    ...

    public void AddEntity(string bar)
    {
        if (!Entities.ContainsKey(bar))
        {
            // bool value true here has no use and is just a placeholder
            Entities.Add(bar, true);
        }
    }

    public string[] GetEntities()
    {
        return Entities.Keys.ToArray();
    }

}
41
reformed

Vous pouvez utiliser HashSet<T> .

Le HashSet<T> La classe fournit des opérations d'ensemble hautes performances. Un ensemble est une collection qui ne contient aucun élément en double et dont les éléments ne sont pas dans un ordre particulier .

84
Habib

réponse d'Habib est excellent, mais pour les environnements multi-threads si vous utilisez un HashSet<T> alors, par conséquent, vous devez utiliser locks pour en protéger l'accès. Je me trouve plus enclin à créer des blocages avec des instructions lock. De plus, locks produit une accélération moins bonne par loi d'Amdahl car l'ajout d'une instruction lock réduit le pourcentage de votre code qui est réellement parallèle.

Pour ces raisons, un ConcurrentDictionary<T,object> correspond à la facture dans les environnements multi-thread. Si vous finissez par en utiliser un, enveloppez-le comme vous l'avez fait dans votre question. Juste new up objects pour ajouter autant de valeurs que nécessaire, car les valeurs ne seront pas importantes. Vous pouvez vérifier qu'il n'y a pas d'instructions lock dans son code source.

Si vous n'aviez pas besoin de mutabilité de la collection, ce serait théorique. Mais votre question implique que vous en avez besoin, car vous avez une méthode AddEntity.

Informations supplémentaires 2017-05-19 - en fait, ConcurrentDictionary le fait utilise des verrous en interne, mais pas lock instructions en soi - il utilise Monitor.Enter (découvrez la méthode TryAddInternal ). Cependant, il semble verrouiller des compartiments individuels dans le dictionnaire, ce qui signifie qu'il y aura moins de conflits que de placer le tout dans une instruction lock.

Donc, dans l'ensemble, ConcurrentDictionary est souvent meilleur pour les environnements multithread.

Il est en fait assez difficile (impossible?) De créer un ensemble de hachage simultané en utilisant uniquement les méthodes Interlocked . J'ai essayé par moi-même et j'ai continué à rencontrer le problème de devoir modifier deux choses en même temps - quelque chose que seul le verrouillage peut faire en général. Une solution de contournement que j'ai trouvée consistait à utiliser des listes à liaison unique pour les compartiments de hachage et à créer intentionnellement des cycles dans une liste lorsqu'un thread devait fonctionner sur un nœud sans interférence d'autres threads; cela entraînerait d'autres threads à se coincer en tournant au même endroit jusqu'à ce que ce thread soit fait avec son nœud et annule le cycle. Bien sûr, techniquement, il n'utilisait pas de verrous, mais il ne s'est pas bien adapté.

3
Matt Thomas