4

是否有任何内置集合类型 ( 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

? 我的问题是,框架中是否有一些行为类似于SomeCollection代码SomeComparer<T>中的内容?

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

要求:

  1. 集合只需要一个简单的可枚举类型和Add方法
  2. 物品顺序很重要
  3. 集合中可能存在重复项

注意:我可以自己写一个,这很简单。有很多关于 SO 帮助的问题。我在问框架本身是否有一个类。

4

3 回答 3

1

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

这里

于 2013-11-08T21:48:45.853 回答
1

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

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

于 2013-11-16T00:48:10.933 回答
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);
    }
}
于 2013-11-08T21:36:19.557 回答