对象的散列码

在前一篇读书笔记里已经提过,如果我们定义的一个类型只重写了Equals而没有重写GetHashCode方法时编译器会发出警告信息。

  一个类型为什么要同时重写Equals方法和GetHashCode方法?这是因为System.Collections.Hashtable类型的实现要求任何两个相等的对象都必须有相同的散列码值。即GetHashCode的行为依赖于Equals方法进行判断。所以,如果我们重写了Equals方法,也应该(必须)重写GetHashCode方法以确保用来判等的算法和用来计算对象散列码的算法一致。

  基本上来讲,当我们向一个Hashtable对象中添加一个“键/值对”时,其中“键对象”的散列码会首先被获取。该散列码指出了“键/值对”应该被存储在哪个“散列桶”中。当Hashtable对象需要查找某个“键”时,它会取得指定的“键对象”散列码。然后在该散列码所标识的那个“散列桶”中进一步查找和指定的“键对象”相等的“键对象”。使用这种存储和搜索的“键”算法意味着如果我们改变了Hashtable中的一个“键对象”,我们在Hashtable中将不能再找到该对象。如果我们改变一个散列表中的“键对象”,我们应该首先删除原来的“键/值对”,然后改变“键对象”,最后再将新的“键/值对”添加到散列表中。

  System.Object中实现的GetHashCode方法对于它的派生类型及其内的字段一无所知。出于这个原因,Object的GetHashCode方法返回的是一个在应用程序域范围内确保唯一的数值。该数值在整个生命周期中保证不会改变。但是,在对象被执行垃圾收集后,这个唯一的数值可以被重新利用作为一个新的对象的散列码。

  System.ValueType中实现的GetHashCode方法使用反射来返回定义在类型中第一个实例字段的散列码。这个简单的实现对于某些值类型来说可能已经够用,但是还是建议最好提供自己的实现。即使我们实现的算法同样返回一个实例字段的散列码,我们的实现也要比ValueType中实现的快一些(why?因为是直接调用的缘故?)下面是ValueType中实现的GetHashCode方法:
   

20162525_1Bpu.gifclass ValueType
20162525_heHU.gif 20162525_b6e0.gif    20162525_j4ov.gif{
20162525_Nnqy.gif      protected override Int32 GetHashCode()
20162525_vIFa.gif 20162525_tCWO.gif       20162525_j4ov.gif{
20162525_Nnqy.gif         FieldInfo[] fields = this.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 
20162525_Nnqy.gif
20162525_Nnqy.gif     if(fields.Length > 0)
20162525_vIFa.gif 20162525_tCWO.gif      20162525_j4ov.gif{
20162525_Nnqy.gif        for(Int32 i = 0;i < fields.Length;i++)
20162525_vIFa.gif 20162525_tCWO.gif         20162525_j4ov.gif{
20162525_Nnqy.gif           Object obj = fields[i].GetValue(this);
20162525_Nnqy.gif           if(null != obj)
20162525_Nnqy.gif             return obj.GetHashCode();
20162525_OpA2.gif        }
20162525_OpA2.gif     } 
20162525_Nnqy.gif
20162525_Nnqy.gif     //如果没有非空字段,为其返回一个与类型相关的唯一值
20162525_Nnqy.gif     return GetMethodTablePtrAsInt(this);
20162525_OpA2.gif      }
20162525_dbQ4.gif   } 
20162525_1Bpu.gif
20162525_1Bpu.gif

   
     如果我们因为某种原因要实现自己的散列表集合,或者编写的任何代码中调用了GetHashCode方法,我们都不应该持久化(persist)散列码值。原因是散列码值可能会改变。例如,一个类型的下一个版本可能会使用不同的算法来计算对象的散列码。


     选择计算类型实例的散列码算法时,我们应该尽力遵循以下原则:


   1、算法应该使所得的数值有一个良好的随机分布,这样散列表可以获得最佳的性能。

   2、算法还可以调用基类型的GetHashCode方法,并将其返回值包含在我们的算法中。但在一般情况下,我们不应该调用Object或ValueType的GetHashCode方法,因为这两个类型的GetHashCode方法实现都不会获得高性能的           散列算法。

   3、算法应该至少使用一个实例字段(以确保唯一性?)。

   4、理想情况下,我们在算法中使用的字段都应该是恒定不变的;也就是说在构造对象时字段被初始化后,它们就不      应该再在对象的生存周期内有任何改变。

   5、算法应该执行的尽可能快。

   6、有着相同值的对象应该返回相同的散列码。例如,两个有着相同文本的String对象应该返回相同的散列码值

转载于:https://my.oschina.net/leoson/blog/103247

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值