【问题标题】:Example usage of HashSet<T>.CreateSetComparerHashSet<T>.CreateSetComparer 的示例用法
【发布时间】:2014-12-09 17:00:23
【问题描述】:

我知道HashSet&lt;T&gt;.SetEquals 方法,但是应该何时以及如何使用CreateSetComparer

documentation 声明:“仅在一个级别检查相等性;但是,您可以将其他级别的比较器链接在一起以执行更深入的相等性测试”

一个简单的例子是什么?

特别是,如果我比较的集合中的每个项目也包含一个 HashSet ,那么CreateSetComparer 的正确用法是什么?

这是我的出发点。我想知道CreateSetComparer方法是否适用以及如何正确使用:

public class Foo : IEquatable<Foo>
{
    public string Label { get; set; }
    public string Value { get; set; }
    public override string ToString() {return String.Format("{0}:{1}", Label, Value); }

    // assume for this example that Label and Value are immutable once set;
    public override int GetHashCode(){ return ToString().GetHashCode(); }
    // simplified equality check; assume it meets my needs for this example;
    public bool Equals(Foo other){ return String.Equals(this.ToString(), other.ToString()); }
}

public class FooGroup : IEquatable<FooGroup>
{
    public int GroupIndex {get; set;}
    public HashSet<Foo> FooCollection {get; set;}

    // -----------------------------
    // Does HashSet.CreateSetComparer somehow eliminate or simplify the following code?
    // -----------------------------
    public override int GetHashCode()
    { 
        int hash = GroupIndex;
        foreach(Foo f in FooCollection)
          hash = hash ^ (f.GetHashCode() & 0x7FFFFFFF);
        return hash;
    }

    public bool Equals(FooGroup other)
    { 
        // ignore missing null checks for this example
        return this.GroupIndex == other.GroupIndex && this.FooCollection.SetEquals(other.FooCollection);
    }
}

public class GroupCollection : IEquatable<GroupCollection>
{
    public string CollectionLabel {get; set;}
    public HashSet<FooGroup> AllGroups {get; set;}

    // -----------------------------
    // Does HashSet.CreateSetComparer somehow eliminate or simplify the following code?
    // -----------------------------
    public override int GetHashCode()
    { 
        int hash = CollectionLabel.GetHashCode();
        foreach(FooGroup g in AllGroups)
          hash = hash ^ (g.GetHashCode() & 0x7FFFFFFF);
        return hash;
    }

    public bool Equals(GroupCollection other)
    { 
        // ignore missing null checks for this example
        return String.Equals(this.CollectionLabel, other.CollectionLabel) && this.AllGroups.SetEquals(other.AllGroups);
    }
}

忽略关于系统设计等的争论,一个简化的用例将是:假设我提取了一组看起来像这样的复杂数据:

var newSetA = new GroupCollection{ ... }
var oldSetA = new GroupCollection{ ... }

我只是想检查一下:

if (newSetA.Equals(oldSetA))
  Process(newSetA);

【问题讨论】:

  • 我正在努力想出一个有用的通用案例。

标签: c# .net hashset


【解决方案1】:

让我们从“CreateSetComparer 什么时候有用”的问题开始?你已经有了一个很好的想法:

特别是,如果我要比较的集合中的每个项目也包含一个 HashSet ,那么 CreateSetComparer 的正确用法是什么?

例如,下一个示例演示了HashSet 使用其默认比较器(仅通过引用进行比较)时的默认行为:

var set1 = new HashSet<HashSet<int>>{
    new HashSet<int>{2,3,4},
    new HashSet<int>{7,8,9}
};
var set2 = new HashSet<HashSet<int>>{
    new HashSet<int>{2,3,4},
    new HashSet<int>{7,8,9},
};

set1.SetEquals(set2).Dump(); // false :-(    
set1.SequenceEqual(set2).Dump(); // false
set1.SequenceEqual(set2, HashSet<int>.CreateSetComparer()).Dump(); // true

也可以将CreateSetComparerSetEquals 一起使用,如下所示:

// the order of elements in the set has been change.
var set1 = new HashSet<HashSet<int>>(HashSet<int>.CreateSetComparer()){
    new HashSet<int>{2,3,4},
    new HashSet<int>{7,8,9}
};
var set2 = new HashSet<HashSet<int>>{
    new HashSet<int>{7,8,9},
    new HashSet<int>{2,3,4},
};

set1.SetEquals(set2).Dump(); // true :-)
set1.SequenceEqual(set2).Dump(); // false
set1.SequenceEqual(set2, HashSet<int>.CreateSetComparer()).Dump(); // false

这是通常的用法,但是 CreateSetComparer 提供了您可以利用的 GetHashCode,尽管这不一定更短/更干净,您已经做了。

// -----------------------------
// Does HashSet.CreateSetComparer somehow eliminate or simplify the following code?
// -----------------------------
private IEqualityComparer<HashSet<FooGroup>> _ecomparer = 
        HashSet<FooGroup>.CreateSetComparer();
public override int GetHashCode()
{ 
    int hash = CollectionLabel.GetHashCode();
    hash ^= _ecomparer.GetHashCode(AllGroups);
    return hash;
}

【讨论】:

  • 太棒了!我无法从文档中推断出这种用法。仅供参考,令我惊讶的是 SetEquals 会因一组嵌套的值类型而失败......我原以为 HashSet 使用默认比较器的行为会在这种情况下递归。
  • @mdisibio,我还添加了一些关于GetHashCode 的内容。 Ps,确实,您可能会争论哪些功能会更有用,但 .NET 的理念是不要让事情复杂化,并且总是 & 对于一切都具有相同的默认行为,没有例外。
【解决方案2】:

我在向字典提供“多个”键时使用它,其中的顺序无关紧要:

var dict = new Dictionary<HashSet<int>, string>(HashSet<int>.CreateSetComparer());
dict[new HashSet<int> { 1, 2 }] = "foo";
dict[new HashSet<int> { 2, 1 }].Dump();

您可以通过使用params 索引器包装它来提供更好的 API:

public class MultiKeyDictionary<TKey, TValue> : IDictionary<HashSet<TKey>, TValue>
{
    private readonly IDictionary<HashSet<TKey>, TValue> _dict;

    public MultiKeyDictionary()
    {
        _dict = new Dictionary<HashSet<TKey>, TValue>(HashSet<TKey>.CreateSetComparer());
    }

    public TValue this[params TKey[] keys]
    {
        get { return _dict[new HashSet<TKey>(keys)]; }
        set { _dict[new HashSet<TKey>(keys)] = value; }
    }

    ...
}


var dict = new MultiKeyDictionary<int, string>();
dict[1, 2] = "foo";
dict[2, 1].Dump();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-05
    • 2011-04-25
    • 2015-04-24
    • 1970-01-01
    • 2016-08-17
    • 1970-01-01
    相关资源
    最近更新 更多