如何使用HashSet <T>作为字典键?

Gra*_*meF 24 .net c# collections

我希望HashSet<T>用作词典的关键:

Dictionary<HashSet<T>, TValue> myDictionary = new Dictionary<HashSet<T>, TValue>();
Run Code Online (Sandbox Code Playgroud)

我想从字典中查找值,HashSet<T>以便包含相同项的两个不同实例将返回相同的值.

HashSet<T>的实现Equals()GetHashCode()似乎并没有这样做(我认为它们只是默认值).我可以覆盖Equals()使用SetEquals()但是怎么样GetHashCode()?我觉得我在这里错过了一些东西......

dig*_*All 38

您可以使用以下提供的set comparer HashSet<T>:

var myDictionary = new Dictionary<HashSet<T>, TValue>(HashSet<T>.CreateSetComparer());
Run Code Online (Sandbox Code Playgroud)

  • 不知道那个存在!看起来很有趣(现在我觉得用自定义答案写答案很愚蠢; p) (4认同)

Cod*_*aos 8

digEmAll的答案显然是实践中更好的选择,因为它使用内置代码而不是重新发明轮子.但我将把它作为一个示例实现.


您可以使用实施IEqualityComparer<HashSet<T>>使用SetEquals.然后将它传递给Dictionary的构造函数.像下面的东西(没有测试):

class HashSetEqualityComparer<T>: IEqualityComparer<HashSet<T>>
{
    public int GetHashCode(HashSet<T> hashSet)
    {
        if(hashSet == null)
           return 0;
        int h = 0x14345843; //some arbitrary number
        foreach(T elem in hashSet)
        {
            h = unchecked(h + hashSet.Comparer.GetHashCode(elem));
        }
        return h;
    }

    public bool Equals(HashSet<T> set1, HashSet<T> set2)
    {
        if(set1 == set2)
            return true;
        if(set1 == null || set2 == null)
            return false;
        return set1.SetEquals(set2);
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这里的哈希函数是可交换的,这很重要,因为集合中元素的枚举顺序是未定义的.

另一个有趣的一点是,您不能只使用,elem.GetHashCode因为当向集合提供自定义相等比较器时,这会产生错误的结果.

  • 只有少数情况下才能保证唯一的哈希值.但是,+和^通常都是组合哈希的糟糕选择.但是我在这种情况下选择了它们,因为它们是可交换的.组合哈希的常见选择(如`hash = hash*primeConstant + elemHash`)在设计上是不可交换的.这里需要交换组合.顺便说一句,MS实现使用`^` (4认同)
  • 我之所以选择^,因为它是通勤的,因为这些值是唯一的,因此x ^ x = 0问题并不相关.但可能添加仍然是一个更好的主意. (2认同)
  • Xor不好的原因之一是重复项被抵消了。对于所有x,x ^ x = 0。因此,例如,您有一个2d向量,并使用哈希函数`X ^ Y`,每个具有`X == Y`的向量都会发生冲突。xor是可交换的,这意味着交换属性的值也会产生冲突。例如`X = 1,Y = 5`与`X = 5,Y = 1`冲突 (2认同)