是否有内置的集合类型或IEqualityComparer <t>用于集合,它基于项目中的项目是否相等?</t>

时间:2013-11-08 20:53:12

标签: c# .net collections equality iequalitycomparer

框架中有IEnumerable<S> 的内置集合类型(IEqualityComparer<T>)或IEnumerable<S>是否有Equals(和GetHashCode相应地)由其中的项目相等来定义?

类似的东西:

var x = new SomeCollection { 1, 2, 3 };
var y = new SomeCollection { 1, 2, 3 };

// so that x.Equals(y) -> true 
// and x.Shuffle().Equals(y) -> false

或者

class SomeComparer<T> : EqalityComparer<IEnumerable<T>> { }

// so that for 
var x = new[] { 1, 2, 3 };
var y = new[] { 1, 2, 3 };
// gives
// new SomeComparer<int>().Equals(x, y) -> true 
// new SomeComparer<int>().Equals(x.Shuffle(), y) -> false

?我的问题是,框架中是否存在行为类似SomeCollectionSomeComparer<T>的内容,如代码中所示?

为什么我需要它:因为我有一个Dictionary<Collection, T>的案例,其中Key部分应该是一个集合,其相等性基于其条目

要求:

  1. 集合只需要一个简单的可枚举类型Add方法
  2. 项目顺序很重要
  3. 集合中可以存在重复的项目
  4. 注意:我可以自己写一个,这很简单。有很多关于SO帮助的问题。我问的是框架本身是否有一个类。

3 个答案:

答案 0 :(得分:1)

保持简单。只需使用接受专门的IEqualityComparer的Dictionary ctor(只需在比较器中实现你的相等逻辑),你就可以了。不需要特殊的收集类型等......

请参阅here

答案 1 :(得分:1)

如果可以的话,最好定义一个自己的不可变集合类,它接受IEqualityComparer<T>作为构造函数参数,并使其EqualsGetHashCode()成员链接到底层集合,而不是试图为此目的定义IEqualityComparer<T>。除此之外,您的不可变集合类将能够缓存其自己的哈希值,并可能缓存其中包含的项的哈希值。这不仅会加速对集合的GetHashCode()的调用,还会加速两个集合之间的比较。如果两个集合的哈希码不相等,那么进一步检查任何东西都没有意义;即使两个集合的哈希码相等,也可能值得检查相应项的哈希码在测试项本身之前是否匹配[注意,通常,在检查相等性之前使用哈希码测试作为早期退出不是特别有用,因为最慢的Equals情况(项目匹配的地方)是哈希码无论如何都要匹配的情况;但是,如果除了最后一个项目之外的所有项目都匹配,那么测试项目的哈希码可能会在人们花时间检查每个项目之前发现不匹配。

从.NET 4.0开始,可以编写一个IEqualityComparer<T>,它可以通过使用ConditionalWeakTable将集合映射到对象来实现缓存哈希值的不可变集合类的性能优势。会缓存有关它们的信息。尽管如此,除非一个人无法使用自定义的不可变集合类,否则我认为这个类可能比这个场景中的IEqualityComparer<T>更好。

答案 2 :(得分:0)

我不相信存在这样的事情。我需要比较两个词典内容的平等,并写了一段时间。

public class DictionaryComparer<TKey, TValue> : EqualityComparer<IDictionary<TKey, TValue>>
{
    public DictionaryComparer()
    {
    }
    public override bool Equals(IDictionary<TKey, TValue> x, IDictionary<TKey, TValue> y)
    {
        // early-exit checks
        if (object.ReferenceEquals(x, y))
            return true;

        if (null == x || y == null)
            return false;

        if (x.Count != y.Count)
            return false;

        // check keys are the same
        foreach (TKey k in x.Keys)
            if (!y.ContainsKey(k))
                return false;

        // check values are the same
        foreach (TKey k in x.Keys)
        {
            TValue v = x[k];
            if (object.ReferenceEquals(v, null))
                return object.ReferenceEquals(y[k], null);

            if (!v.Equals(y[k]))
                return false;
        }
        return true;
    }

    public override int GetHashCode(IDictionary<TKey, TValue> obj)
    {
        if (obj == null)
            return 0;

        int hash = 0;

        foreach (KeyValuePair<TKey, TValue> pair in obj)
        {
            int key = pair.Key.GetHashCode(); // key cannot be null
            int value = pair.Value != null ? pair.Value.GetHashCode() : 0;
            hash ^= ShiftAndWrap(key, 2) ^ value;
        }

        return hash;
    }

    private static int ShiftAndWrap(int value, int positions)
    {
        positions = positions & 0x1F;

        // Save the existing bit pattern, but interpret it as an unsigned integer. 
        uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
        // Preserve the bits to be discarded. 
        uint wrapped = number >> (32 - positions);
        // Shift and wrap the discarded bits. 
        return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) | wrapped), 0);
    }
}