2013-07-22 36 views
7

Tôi có một HashSet của objs với obj được định nghĩa như vậy:Cách tốt nhất tìm kiếm 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; 
    } 
} 

này hoạt động tốt nhưng tôi đang gặp rắc rối lấy từ HashSet khi HashSet.Add(obj) lợi nhuận sai. Điều gì sẽ là cách tốt nhất để truy xuất số _id của Obj đã được chứa trong số HashSet trong trường hợp này?

+0

Đôi '_'s? Sai lầm hay không? –

+2

Bất kỳ lý do nào cho các biến riêng tư. Bạn chỉ có thể làm 'public int ID {get; set;}' – gunr2171

+0

@newStackExchangeInstance Xin lỗi đó là lỗi đánh máy. – ashishduh

Trả lời

5

Cách tôi nhìn thấy: tổng + mô tả (được sử dụng cho hashcode, bằng) = khóa và _id (những gì bạn muốn truy xuất) = giá trị.

Kịch bản chỉ rõ một từ điển chứ không phải là bộ băm .... bộ không có nghĩa là tra cứu/truy xuất tùy ý.

+0

Nhưng OP không thực sự muốn một từ điển, họ muốn lấy một phần tử * kinh điển *. Chắc chắn, điều này có thể được phrased như là một bản đồ. Nhưng thiết lập tra cứu sẽ đơn giản hơn nhiều. (Nhưng tôi đồng ý rằng sử dụng một 'Dictionary' là giải pháp thực tế tốt nhất ở đây). –

+0

Tôi đã kết thúc bằng từ điển. Không có nhiều ý nghĩa hợp lý cho việc thiết lập dữ liệu của tôi nhưng thực tế nó có ý nghĩa như Konrad Rudolph nói. – ashishduh

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

Một cách khác để làm điều này là sử dụng một từ điển (phím giá trị tương đương):

(giả sử bạn đã chuyển đổi nó):

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

Vấn đề với giải pháp này là sự phức tạp cho việc lặp lại (Được sử dụng với 'Đầu tiên') –

+0

Lưu ý rằng toàn bộ điểm sử dụng một' HashSet' là để tránh thực hiện tìm kiếm tuyến tính. – Servy

+1

@Servy Với một bộ băm thích hợp có thể. Không phải với 'RetardedHashSet' mà tàu với .NET dưới bí danh' HashSet'. –

0

Tôi đã có rắc rối với tình trạng này trong quá khứ. Cấp, tôi đã sử dụng từ điển < TKey, TValue >, giúp việc lấy đối tượng dựa trên khóa dễ dàng hơn. Khi bạn ghi đè mã băm, một vấn đề là Hashtables và lưu trữ bản ghi đó theo các giá trị INITIAL. Vì vậy, nếu bạn fiddle với đối tượng một chút, bạn sẽ không còn có thể phục hồi đối tượng vì mã băm đã thay đổi. Vì vậy, các trick mà tôi sử dụng là phải có một mã số nguyên băm với một phương pháp riêng biệt như

private hashcode; 

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

} 

Bằng cách đó, bạn có thể kiểm soát khi hashcode được cập nhật, vì vậy bạn vẫn có thể tìm thấy đối tượng cũ của bạn. xóa nó khỏi từ điển, sau đó cập nhật đối tượng của bạn, sau đó lưu trữ đối tượng đã sửa đổi.

Nhưng người theo chủ nghĩa thuần túy sẽ không thích điều đó vì nó có nghĩa là kiểm tra bình đẳng nghiêm ngặt và thử nghiệm băm sẽ không hoạt động chính xác trên các đối tượng đã sửa đổi mà chưa cập nhật băm. Vì vậy, thay vào đó, bạn chỉ có thể theo dõi mã băm cũ dưới dạng thuộc tính riêng biệt chỉ được cập nhật khi bạn thêm mã đó vào từ điển.

private int oldHashcode; 

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

Và khi bạn thêm vào từ điển:

item.OldHashCode = item.GetHashCode(); 

Và để lấy

item = myDictionary[item.OldHashCode]; 

hoặc bất cứ điều gì.

+0

Đó không phải là câu hỏi thực sự của OP mặc dù đó là sự thật. Tuy nhiên, trong trường hợp OP, 'Id' chỉ đơn giản là không tạo thành một phần của bản sắc đối tượng. Phần còn lại vẫn không thay đổi và giá trị băm của đối tượng cũng vậy. –

+0

Ah, tôi cho rằng anh ta đã biết làm thế nào để lấy một đối tượng ra khỏi HashSet về nguyên tắc, nhưng đã có vấn đề. – Ted

1

Bạn có thể xác định loại bộ sưu tập của riêng bạn mà được xây dựng trên Dictionary<TKey, TValue> và cung cấp một phương pháp GetOrAdd (tương tự như GetOrAdd của 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; 
    } 
} 

Để sử dụng, bạn sẽ gọi:

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

Để duy trì khả năng tương thích với mã hiện tại của bạn, bạn có thể mở rộng lớp này để triển khai ICollection<T> (hoặc, tốt hơn là, ISet<T>) quá:

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(); 
    } 
} 
Các vấn đề liên quan