2013-07-22 14 views
7

mam HashSet z OBJS z Celu zdefiniowany jako takie:Najlepszy sposób poszukiwania HashSet

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; 
    } 
} 

Działa to dobrze, ale mam problemy z dostępem z HashSet gdy HashSet.Add(obj) zwraca false. Jaki byłby najlepszy sposób pobrania _id z Obj, który jest już zawarty w HashSet w takim przypadku?

+0

Podwójne '_'? Błąd czy nie? –

+2

Każdy powód zmiennych prywatnych. Możesz po prostu zrobić 'public int ID {get; set;}' – gunr2171

+0

@newStackExchangeInstance Przepraszam, że był to literówka. – ashishduh

Odpowiedz

5

Sposób, w jaki go widzę: suma + opis (używany do kodu hash, równa się) = klucz i _id (to, co chcesz odzyskać) = wartość.

Scenariusz wyraźnie wskazuje na słownik, a nie hashset ... zestawy nie są przeznaczone do arbitralnego wyszukiwania/pobierania.

+0

Ale OP tak naprawdę nie potrzebuje słownika, chce odzyskać * element kanoniczny *. Oczywiście, można to sformułować jako mapowanie. Ale ustawienie wyszukiwania byłoby znacznie prostsze. (Ale zgadzam się, że używanie "Słownika" jest tutaj najlepszym praktycznym rozwiązaniem). –

+0

Skończyłem na Słowniku. Nie ma tak dużego sensu logicznego, biorąc pod uwagę moje ustawienia danych, ale praktycznie ma sens, jak mówi Konrad Rudolph. – ashishduh

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

Innym sposobem, aby to zrobić jest użycie słowniku (klawisze równe wartości)

(zakładając, że zostały przekształcone go):

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

Problem z tym rozwiązaniem polega na jego złożoności dla iteracji (używane z 'First'). –

+0

Zauważ, że cały sens używania' HashSet' polega na unikaniu wyszukiwania liniowego. – Servy

+1

@Servy Z odpowiednim zestawem hash. Nie z 'RetardedHashSet', który jest dostarczany z .NET pod aliasem' HashSet'. –

0

miałem kłopotów z tej sytuacji przeszłość. Oczywiście użyłem Słownika < TKey, TValue >, co ułatwia uzyskanie obiektu na podstawie klucza. Kiedy nadpisujesz hashcode, jednym z problemów jest to, że Hashtables i tak przechowują rekord zgodnie z wartościami INITIAL. Więc jeśli trochę skrzypiesz z obiektem, nie będziesz w stanie odzyskać obiektu, ponieważ zmienił się kod skrótu. Więc trik, który kiedyś był mieć całkowitą kod hash z oddzielną metodę jak

private hashcode; 

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

} 

ten sposób można kontrolować, kiedy hashcode jest aktualizowany, tak aby można było jeszcze znaleźć swój stary obiekt. usuń go ze słownika, następnie zaktualizuj obiekt, a następnie zapisz zmodyfikowany obiekt.

Ale purystom się to nie spodoba, ponieważ oznacza to, że rygorystyczne testy równości i testy hash nie będą działać poprawnie na zmodyfikowanych obiektach, które nie zostały zaktualizowane. Zamiast tego możesz po prostu śledzić stary kod skrótu jako oddzielną właściwość, która jest aktualizowana tylko podczas dodawania go do słownika.

private int oldHashcode; 

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

A jeśli dodać do słownika:

item.OldHashCode = item.GetHashCode(); 

I odzyskać

item = myDictionary[item.OldHashCode]; 

czy cokolwiek innego.

+0

To nie jest tak naprawdę pytanie OP, chociaż to prawda. W przypadku OP jednak "Id" po prostu nie stanowi części tożsamości obiektu. Reszta pozostaje niezmieniona, podobnie jak wartość skrótu obiektu. –

+0

Ach, założyłem, że już wiedział, jak w zasadzie usunąć obiekt z HashSet, ale miał problemy. – Ted

1

Można zdefiniować własny typ kolekcji, który opiera się na Dictionary<TKey, TValue> i zapewnia GetOrAdd metodę (podobny do GetOrAdd z ConcurrentDictionary<TKey, TValue>):

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; 
    } 
} 

Do tego zastosowania, można by nazwać:

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; 
} 

Aby zachować zgodność z bieżącym kodem, można rozszerzyć tę klasę do implementacji ICollection<T> (lub, najlepiej, ISet<T>) też:

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(); 
    } 
}