100 votes

Comment récupérer un élément réel de HashSet<T>?

J'ai lu cette question sur la raison pour laquelle il n'est pas possible, mais je n'ai pas trouvé de solution au problème.

J'aimerais récupérer un élément d'un HashSet . Je cherche une méthode qui aurait cette signature :

/// 
/// Détermine si cet ensemble contient un élément égal à , 
/// selon le mécanisme de comparaison utilisé lors de la création de l'ensemble. 
/// L'ensemble n'est pas modifié. Si l'ensemble contient un élément égal à 
/// , alors l'élément de l'ensemble est renvoyé.
/// 
bool TryGetItem(T item, out T foundItem);

Rechercher l'élément dans un ensemble avec une telle méthode serait en O(1). La seule façon de récupérer un élément d'un HashSet est d'énumérer tous les éléments, ce qui est en O(n).

Je n'ai trouvé aucun contournement à ce problème autre que de créer mon propre HashSet ou d'utiliser un Dictionary. Une autre idée ?

Remarque :
Je ne veux pas vérifier si le HashSet contient l'élément. Je veux obtenir la référence de l'élément stocké dans le HashSet car j'ai besoin de le mettre à jour (sans le remplacer par une autre instance). L'élément que je passerais à TryGetItem serait égal (selon le mécanisme de comparaison que j'ai passé au constructeur) mais il ne s'agirait pas de la même référence.

72voto

JPE Points 831

C'est réellement une énorme omission dans l'ensemble des collections. Vous auriez besoin soit d'un dictionnaire de clés uniquement, soit d'un HashSet qui permet la récupération des références d'objets. Tellement de personnes l'ont demandé, pourquoi cela ne se corrige pas reste un mystère pour moi.

Sans les bibliothèques tierces, la meilleure solution de contournement est d'utiliser Dictionary avec des clés identiques aux valeurs, puisque Dictionary stocke ses entrées sous forme de table de hachage. En termes de performances, c'est la même chose que HashSet, mais cela gaspille de la mémoire bien sûr (taille d'un pointeur par entrée).

Dictionary myHashedCollection;
...
if(myHashedCollection.ContainsKey[item])
    item = myHashedCollection[item]; //remplacer le doublon
else
    myHashedCollection.Add(item, item); //ajouter un élément précédemment inconnu
...
//travailler avec l'élément unique

46voto

Evdzhan Mustafa Points 745

Ce que vous demandez a été ajouté à .NET Core il y a un an, et a été récemment ajouté à .NET 4.7.2:

Dans .NET Framework 4.7.2, nous avons ajouté quelques APIs aux types de Collection standard qui permettront de nouvelles fonctionnalités comme suit.
- "TryGetValue" est ajouté à SortedSet et HashSet pour correspondre au modèle Try utilisé dans d'autres types de collection.

La signature est la suivante (trouvée dans .NET 4.7.2 et versions ultérieures) :

    //
    // Résumé :
    //     Recherche dans l'ensemble une valeur donnée et renvoie la valeur égale trouvée, le cas échéant.
    //
    // Paramètres :
    //   equalValue :
    //     La valeur à rechercher.
    //
    //   actualValue :
    //     La valeur de l'ensemble que la recherche a trouvée, ou la valeur par défaut de T lorsque
    //     la recherche ne donne aucune correspondance.
    //
    // Renvoie :
    //     Une valeur indiquant si la recherche a été réussie.
    public bool TryGetValue(T equalValue, out T actualValue);

P.S.: Au cas où vous seriez intéressé, il y a une fonction connexe qu'ils ajoutent dans le futur - HashSet.GetOrAdd(T).

12voto

Douglas Points 25145

Cette méthode a été ajoutée à .NET Framework 4.7.2 (et à .NET Core 2.0 avant cela); voir HashSet.TryGetValue. Citant la source:

/// 
/// Recherche la valeur dans l'ensemble et renvoie la valeur égale trouvée, le cas échéant.
/// 
/// La valeur à rechercher.
/// 
/// 
/// La valeur de l'ensemble que la recherche a trouvée, ou la valeur par défaut
/// de  lorsque la recherche n'a donné aucune correspondance.
/// Une valeur indiquant si la recherche a réussi.
/// 
/// Cela peut être utile lorsque vous souhaitez réutiliser une référence précédemment stockée au lieu d'en construire une nouvelle 
/// (afin de permettre davantage de partage de références), ou pour rechercher
/// une valeur ayant des données plus complètes que la valeur que vous avez actuellement, bien que leurs
/// fonctions de comparaison indiquent qu'elles sont égales.
/// 
public bool TryGetValue(T equalValue, out T actualValue)

5voto

mp666 Points 65

Que diriez-vous de surcharger le comparateur d'égalité de chaîne :

  class StringEqualityComparer : IEqualityComparer
{
    public string val1;
    public bool Equals(String s1, String s2)
    {
        if (!s1.Equals(s2)) return false;
        val1 = s1;
        return true;
    }

    public int GetHashCode(String s)
    {
        return s.GetHashCode();
    }
}
public static class HashSetExtension
{
    public static bool TryGetValue(this HashSet hs, string value, out string valout)
    {
        if (hs.Contains(value))
        {
            valout=(hs.Comparer as StringEqualityComparer).val1;
            return true;
        }
        else
        {
            valout = null;
            return false;
        }
    }
}

Et ensuite déclarez le HashSet comme :

HashSet hs = new HashSet(new StringEqualityComparer());

2voto

Vulovic Vukasin Points 1168

D'accord, donc, vous pouvez le faire de cette manière

VotreObjet x = votreHashSet.Where(w => w.Name.Contains("strin")).FirstOrDefault();

Ceci est pour obtenir une nouvelle instance de l'objet sélectionné. Pour mettre à jour votre objet, vous devriez utiliser :

votreHashSet.Where(w => w.Name.Contains("strin")).FirstOrDefault().MyProperty = "quelque chose";

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X