8

我有一个 Obj 的 HashSet,其中 Obj 定义如下:

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

这很好用,但是当HashSet.Add(obj)返回 false 时,我无法从 HashSet 中检索。在这种情况下,检索已包含在中_id的最好的方法是什么?ObjHashSet

4

4 回答 4

5

我看到它的方式:总和 + 描述(用于哈希码,等于)= 键和 _id(您要检索的内容)= 值。

该场景清楚地指向字典而不是哈希集......集合并不意味着任意查找/检索。

于 2013-07-22T21:03:35.217 回答
3
myHashSet.First(x => x.Equals(myItemToRetrieve)).Id;

另一种方法是使用字典(键值相等):

(假设您已转换它):

Obj temp;
if (theDictionary.TryGetValue(myItemToRetrieve, out temp))
{
    int ID = temp.Id;
}
else
{
    theDictionary[myItemToRetrieve] = myItemToRetrieve;
}
于 2013-07-22T20:47:21.380 回答
1

您可以定义自己的集合类型,该集合类型基于Dictionary<TKey, TValue>并提供GetOrAdd方法(类似于GetOrAddof 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;
    }
}

要使用它,您可以调用:

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

为了保持与当前代码的兼容性,您可以扩展此类以实现ICollection<T>(或者,最好是ISet<T>):

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();
    }
}
于 2013-07-22T21:11:04.047 回答
0

过去我在这种情况下遇到过麻烦。当然,我使用的是 Dictionary<TKey,TValue>,它可以更轻松地根据键获取对象。当您覆盖哈希码时,一个问题是哈希表等根据初始值存储记录。因此,如果您稍微摆弄一下对象,您将无法再恢复该对象,因为哈希码已更改。所以我使用的技巧是有一个整数哈希码和一个单独的方法,比如

private hashcode;

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

}

这样,您可以控制哈希码何时更新,以便您仍然可以找到旧对象。从字典中删除它,然后更新您的对象,然后存储修改后的对象。

但纯粹主义者不会喜欢这样,因为这意味着严格的相等性测试和哈希测试无法在未更新哈希的已修改对象上正常工作。因此,您可以将旧的哈希码作为单独的属性进行跟踪,仅在您将其添加到字典时才会更新。

private int oldHashcode;

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

当您添加到字典时:

item.OldHashCode = item.GetHashCode();

并检索

item = myDictionary[item.OldHashCode];

管他呢。

于 2013-07-22T20:55:03.867 回答