C# 为什么C不为集合实现GetHashCode?
我正在将一些东西从Java移植到C#。在Java中,C# 为什么C不为集合实现GetHashCode?,c#,java,collections,hashcode,gethashcode,C#,Java,Collections,Hashcode,Gethashcode,我正在将一些东西从Java移植到C#。在Java中,ArrayList的hashcode取决于其中的项目。在C语言中,我总是从列表中得到相同的哈希代码 为什么会这样 对于我的一些对象,hashcode需要不同,因为它们的list属性中的对象使对象不相等。我希望hashcode对于对象的状态总是唯一的,并且只有当对象相等时才等于另一个hashcode。我错了吗?为什么太哲学化了。创建helper方法(可能是扩展方法)并根据需要计算hashcode。可能是XOR元素的哈希代码哈希代码不可能在大多数非
ArrayList
的hashcode
取决于其中的项目。在C语言中,我总是从列表中得到相同的哈希代码
为什么会这样
对于我的一些对象,hashcode需要不同,因为它们的list属性中的对象使对象不相等。我希望hashcode对于对象的状态总是唯一的,并且只有当对象相等时才等于另一个hashcode。我错了吗?为什么太哲学化了。创建helper方法(可能是扩展方法)并根据需要计算hashcode。可能是XOR元素的哈希代码哈希代码不可能在大多数非平凡类的所有变体中都是唯一的。在C#中,列表相等的概念与Java中的不同(请参阅),因此哈希代码的实现也不同-它反映了C#列表相等。是的,您错了。在Java和C#中,相等意味着拥有相同的哈希代码,但相反(不一定)是真的
有关更多信息,请参阅。您只错了一部分。当你认为相等的hashcode意味着相等的对象,但是相等的对象必须有相等的hashcode,这意味着如果hashcode不同,那么对象也一样,你肯定错了 为了正确工作,哈希代码必须是不可变的–对象的哈希代码永远不能更改
如果对象的哈希代码确实发生了更改,则包含该对象的任何词典都将停止工作
因为集合不是不可变的,所以它们不能实现GetHashCode
相反,它们继承了默认的GetHashCode
,它为对象的每个实例返回一个(希望是)唯一的值。(通常基于内存地址)核心原因是性能和人性人们倾向于认为哈希运算速度快,但通常需要至少遍历一次对象的所有元素
示例:如果在哈希表中使用字符串作为键,则每个查询的复杂性都是O(| s |)-使用2倍长的字符串,这将花费至少两倍的成本。想象一下,这是一个完整的树(只是一个列表列表)-哎呀:-)
如果完整的深度散列计算是一个集合的标准操作,那么很大一部分编程人员会在无意中使用它,然后责怪框架和虚拟机太慢对于像完全遍历这样昂贵的东西,程序员必须意识到复杂性是至关重要的。实现这一点的唯一方法是确保您必须编写自己的代码。这也是一个很好的威慑:——)
另一个原因是更新战术。动态计算和更新散列与每次进行完整计算相比,需要根据手头的具体情况进行判断调用
Immutability只是一种学术上的逃避——人们使用散列作为一种更快地检测变化的方法(例如文件散列),也使用散列来检测不断变化的复杂结构。除了101个基本功能外,哈希还有更多的用途关键是,对于复杂对象的哈希,使用什么必须是基于具体情况的判断调用。
使用对象的地址(实际上是一个句柄,因此在GC之后不会更改)由于散列实际上是任意可变对象的散列值保持不变的情况:-)C这样做的原因是它很便宜,并且再次促使人们自己计算。散列代码必须依赖于所使用的等式定义,因此如果a==B
则a.GetHashCode()==B.GetHashCode()
(但不一定是相反的;A.GetHashCode()==B.GetHashCode()
不包含A==B
)
默认情况下,值类型的相等定义基于其值,引用类型的相等定义基于其标识(即,默认情况下引用类型的实例仅等于其自身),因此,值类型的默认哈希代码取决于它包含的字段的值*,而引用类型的默认哈希代码则取决于标识。事实上,因为我们理想情况下希望非相等对象的哈希代码不同,特别是在低阶位(最有可能影响重新哈希的值),我们通常希望两个等价但不相等的对象具有不同的哈希
由于对象将保持与自身相等,因此也应该清楚的是,GetHashCode()
的默认实现将继续具有相同的值,即使对象发生了变化(即使对于可变对象,标识也不会发生变化)
现在,在某些情况下,引用类型(或值类型)重新定义相等。这方面的一个例子是字符串,例如“ABC”==“AB”+“C”
。虽然比较了两个不同的字符串实例,但它们被认为相等。在本例中GetHashCode()
必须被重写,以便该值与定义相等的状态(在本例中为包含的字符序列)相关
虽然对同样不可变的类型执行此操作更为常见,但出于各种原因,GetHashCode()
并不依赖于不可变性
在面对易变性时必须保持一致-更改我们在确定散列时使用的值,并且散列必须相应地更改。但是,请注意,如果我们使用此易变对象作为使用散列的结构的键,这是一个问题,因为对该对象进行变异会改变其应停止的位置红色,而不将其移动到该位置(在集合中对象的位置取决于其值的任何其他情况下也是如此-例如,如果我们对列表进行排序,然后对列表中的一个项目进行变异,则列表将不再排序)。
ArrayList a = new ArrayList();
ArrayList b = new ArrayList();
for(int i = 0; i != 10; ++i)
{
a.Add(i);
b.Add(i);
}
return a == b;//returns false
ArrayList c = new ArrayList();
for(short i = 0; i != 10; ++i)
{
c.Add(i);
}
public class ValueEqualList : ArrayList, IEquatable<ValueEqualList>
{
/*.. most methods left out ..*/
public Equals(ValueEqualList other)//optional but a good idea almost always when we redefine equality
{
if(other == null)
return false;
if(ReferenceEquals(this, other))//identity still entails equality, so this is a good shortcut
return true;
if(Count != other.Count)
return false;
for(int i = 0; i != Count; ++i)
if(this[i] != other[i])
return false;
return true;
}
public override bool Equals(object other)
{
return Equals(other as ValueEqualList);
}
public override int GetHashCode()
{
int res = 0x2D2816FE;
foreach(var item in this)
{
res = res * 31 + (item == null ? 0 : item.GetHashCode());
}
return res;
}
}
public class ArrayListEqComp : IEqualityComparer<ArrayList>
{//we might also implement the non-generic IEqualityComparer, omitted for brevity
public bool Equals(ArrayList x, ArrayList y)
{
if(ReferenceEquals(x, y))
return true;
if(x == null || y == null || x.Count != y.Count)
return false;
for(int i = 0; i != x.Count; ++i)
if(x[i] != y[i])
return false;
return true;
}
public int GetHashCode(ArrayList obj)
{
int res = 0x2D2816FE;
foreach(var item in obj)
{
res = res * 31 + (item == null ? 0 : item.GetHashCode());
}
return res;
}
}