2013-07-22 11 views
7

Ich habe eine HashSet von Objs mit Obj als solche definiert:Der beste Weg, HashSet des Suchens

public class Obj 
{ 
    private int _id; 
    private string _desc; 
    private int _sum; 

    public int Id 
    { 
     get { return _id; } 
     set { _id = value; } 
    } 

    public string Description 
    { 
     get { return _desc; } 
     set { _desc = value; } 
    } 

    public int Sum 
    { 
     get { return _sum; } 
     set { _sum = value; } 
    } 

    public Obj(int id, string desc, int sum) 
    { 
     _id = id; 
     _sum = sum; 
     _desc = desc; 
    } 

    public override bool Equals(Obj other) 
    { 
     return this._sum == other._sum 
      && this._desc == other._desc; 
    } 

    public override int GetHashCode() 
    { 
     int hash = 13; 
     hash = (hash * 7) + _sum.GetHashCode(); 
     hash = (hash * 7) + _desc.GetHashCode(); 

     return hash; 
    } 
} 

Dies funktioniert gut, aber ich habe Probleme aus dem HashSet Abrufen wenn HashSet.Add(obj) false zurück. Was wäre der beste Weg, um die _id der Obj, die bereits in der HashSet in einem solchen Fall enthalten ist, abzurufen?

+0

Doppel '_'s? Fehler oder nicht? –

+2

Jeder Grund für die privaten Variablen. Sie könnten einfach 'public int ID {get; set;}' – gunr2171

+0

@newStackExchangeInstance Entschuldigen, das war ein Tippfehler. – ashishduh

Antwort

5

Die Art, wie ich es sehe: Summe + Beschreibung (für Hashcode, equals) = Schlüssel und _id (was Sie abrufen möchten) = Wert.

Das Szenario verweist eindeutig auf ein Wörterbuch statt auf ein Hashset .... Mengen sind nicht für willkürliches Suchen/Suchen bestimmt.

+0

Aber OP möchte nicht wirklich ein Wörterbuch, sie wollen ein * kanonisches Element * abrufen. Sicher, das kann als Mapping formuliert werden. Aber Set-Lookup wäre viel einfacher. (Aber ich stimme zu, dass die Verwendung eines Wörterbuchs die beste praktische Lösung ist). –

+0

Ich endete mit Wörterbuch. Logisch gesehen macht das bei meiner Datenkonfiguration keinen Sinn, aber praktisch macht es Sinn, wie Konrad Rudolph sagt. – ashishduh

3
myHashSet.First(x => x.Equals(myItemToRetrieve)).Id; 

Ein anderer Weg, dies zu tun, ist ein Dictionary (Schlüssel gleiche Werte) zu verwenden:

(vorausgesetzt, Sie haben es umgerechnet):

Obj temp; 
if (theDictionary.TryGetValue(myItemToRetrieve, out temp)) 
{ 
    int ID = temp.Id; 
} 
else 
{ 
    theDictionary[myItemToRetrieve] = myItemToRetrieve; 
} 
+0

Das Problem mit dieser Lösung ist ihre Komplexität für die Iteration (verwendet mit 'First') –

+0

Beachten Sie, dass der Sinn eines' HashSet's darin besteht, lineare Suchen zu vermeiden. – Servy

+1

@Servy Mit einem richtigen Hash-Set vielleicht. Nicht mit 'RetardedHashSet', das mit .NET unter dem Alias' HashSet' ausgeliefert wird. –

0

ich Schwierigkeiten gehabt haben, mit dieser Situation in die Vergangenheit. Zugegeben, ich benutzte ein Dictionary < TKey, TValue >, das es einfacher macht, das Objekt basierend auf einem Schlüssel zu bekommen. Wenn Sie den Hashcode überschreiben, besteht ein Problem darin, dass Hashtabellen und solche den Datensatz gemäß den INITIAL-Werten speichern. Wenn Sie also etwas mit dem Objekt herumspielen, können Sie das Objekt nicht mehr wiederherstellen, weil sich der Hash-Code geändert hat. So ist der Trick, den ich verwenden, war einen ganze Zahl Hash-Code mit einem separaten Verfahren zu haben, wie

private hashcode; 

public void UpdateHashCode(){ 
    hashcode = // your original logic here. 

} 

Auf diese Weise können Sie steuern, wenn der Hash-Code aktualisiert wird, so dass Sie immer noch Ihr altes Objekt finden. Entferne es aus dem Wörterbuch, aktualisiere dann dein Objekt und speichere das modifizierte Objekt.

Aber Puristen werden das nicht mögen, da es bedeutet, dass strikte Gleichheitsprüfung und Hash-Tests nicht korrekt auf modifizierten Objekten funktionieren, deren Hash nicht aktualisiert wurde. Stattdessen können Sie den alten Hash-Code nur als separate Eigenschaft verfolgen, die nur aktualisiert wird, wenn Sie sie dem Wörterbuch hinzufügen.

private int oldHashcode; 

public int OldHashcode{ 
    get{ 
     return oldHashCode; 
    } 
    set { 
     oldHashCode = value; 
    } 
} 

Und wenn Sie zum Wörterbuch hinzufügen:

item.OldHashCode = item.GetHashCode(); 

Und

item = myDictionary[item.OldHashCode]; 

oder was auch immer abzurufen.

+0

Das ist nicht wirklich OP Frage, obwohl es wahr ist. Im Falle von OP ist die "Id" jedoch nicht Teil der Objektidentität. Der Rest bleibt unverändert, ebenso der Hashwert des Objekts. –

+0

Ah, ich nahm an, dass er bereits wusste, wie man ein Objekt aus dem HashSet im Prinzip zurückbekommt, hatte aber Probleme. – Ted

1

Sie könnten Ihre eigene Sammlung Typ definieren, die auf Dictionary<TKey, TValue> und ein GetOrAdd Verfahren (zum GetOrAdd von ConcurrentDictionary<TKey, TValue> ähnlich) baut:

public partial class HashDictionary<T> : Dictionary<T, T> 
{ 
    public T GetOrAdd(T newItem) 
    { 
     T oldItem; 
     if (this.TryGetValue(newItem, out oldItem)) 
      return oldItem; 

     this.Add(newItem, newItem); 
     return newItem; 
    } 
} 

Um dies zu nutzen, würden Sie rufen:

Obj presentO = myHashDictionary.GetOrAdd(newO); 
if (presentO == newO) 
{ 
    // The item was not already present, and has been added. 
} 
else 
{ 
    // A collision occurred, and presentO points to the existent item. 
    int alreadyContainedID = presentO.ID; 
} 

Um die Kompatibilität mit Ihrem aktuellen Code beizubehalten, können Sie diese Klasse so erweitern, dass sie (oder vorzugsweise ISet<T>) implementiert:

public partial class HashDictionary<T> : ICollection<T> 
{   
    public void Add(T item) 
    { 
     this.GetOrAdd(item); 
    } 

    public bool Contains(T item) 
    { 
     return this.ContainsKey(item); 
    } 

    public void CopyTo(T[] array, int arrayIndex) 
    { 
     this.Keys.CopyTo(array, arrayIndex); 
    } 

    public bool IsReadOnly 
    { 
     get { return false; } 
    } 

    public new IEnumerator<T> GetEnumerator() 
    { 
     return this.Keys.GetEnumerator(); 
    } 
}