在这里要非常小心。如果你GetHashCode
为一个(或类似的集合)创建一个方法List<T>
,那么它大概会做这样的事情:
public override int GetHashCode()
{
int hash = 13;
foreach (var t in this)
{
// X is an operation (undefined here) that somehow combines
// the previous hash value and the item's hash value
hash = hash X t.GetHashCode();
}
return hash;
}
(我建议使用Jenkins 散列来计算散列码。还要查看Wang 散列(或位混合器)。)
除非您第一次计算该值并将其缓存,否则每次GetHashCode
调用时您最终都会遍历所有项目。
所以你已经为你的集合创建了一个GetHashCode
andEquals
并且你把一个实例放入了一个Dictionary
. 现在您必须非常小心不要更改集合(即不要添加或删除任何项目)或集合内的任何项目。否则 的值GetHashCode
会改变,字典将不再起作用。
我强烈建议,如果您想使用集合作为字典的键,请确保该集合是不可变的。
要考虑的另一件事。列表相等的概念并不像您所说的那么简单。例如,列表[1, 2, 3, 4, 5]
和[5, 1, 3, 4, 2]
是否相等?这取决于您对平等的定义。当然A.Union(B) == A.Intersect(B)
,这意味着如果您对相等的定义是“包含相同的项目”,则它们是相等的。但是如果顺序很重要,那么列表就不一样了。
如果您的定义是“包含相同的项目”,那么我上面显示的哈希码计算将不起作用,因为哈希码计算是依赖于顺序的。所以如果你想计算这些列表的哈希码,你必须先对它们进行排序。
如果列表不能包含重复项,那么计算相等性就是创建一个列表的哈希集并从该哈希集中的另一个列表中查找每个项目。如果列表可以包含重复项,那么您要么必须对它们进行排序以确定相等性,要么使用某种带有计数的字典。这两者都意味着列表中包含的对象将实现某种形式的相等比较器等。
并且一些平等的定义根本不考虑重复。也就是说,[1, 2, 3]
将等于[3, 3, 3, 2, 1, 1]
。
考虑到平等的不同差异以及在定义 的行为时允许这些差异以及更多的努力List<T>
,我可以理解为什么设计集合类的人没有实现值平等。特别是考虑到List<T>
在字典或哈希表中使用或类似的集合作为键是非常罕见的。