http://blog.csdn.net/wyfde123/archive/2011/05/05/6397130.aspx#

关于C#的hashcode一直有些疑问:

1、为什么重写了Equals方法还要同时重写GetHashCode方法?

2、哈希码用在Hashtable和Dictionary中用于索引项,但是哈希码是一样的情况下怎么办呢?

3、GetHashCode方法为什么要放在Object里面?

解答 
带着这些疑问,查阅了一下MSDN,摘抄了几段

哈希代码是一个用于在相等测试过程中标识对象的数值。 它还可以作为一个集合中的对象的索引。

.NET Framework 不保证 GetHashCode 方法的默认实现以及它所返回的值在不同版本的 .NET Framework 中是相同的。 因此,在进行哈希运算时,该方法的默认实现不得用作唯一对象标识符。

哈希函数必须具有以下特点:

如果两个对象的 Equals 比较结果相等,则每个对象的 GetHashCode 方法都必须返回同一个值。 但是,如果两个对象的比较结果不相等,则这两个对象的 GetHashCode 方法不一定返回不同的值。

一个对象的 GetHashCode 方法必须总是返回同一个哈希代码,但前提是没有修改过对象状态,对象状态用来确定对象的 Equals 方法的返回值。请注意,这仅适用于应用程序的当前执行,再次运行该应用程序时可能会返回另一个哈希代码。

为了获得最佳性能,哈希函数必须为所有输入生成随机分布。

从特点一能够得出第一个问题的答案,Object中的Equals方法只是简单判断两个对象是不是引用同一个对象,而由于Object中没有任何实例字段GetHashCode方法返回的哈希码能作为全局唯一的标识,但是值类型基类的GetHashCode方法则使用了反射,效率也比较低。

下面来解答第二个问题

首先定义一个重写了GetHashCode方法的类,这里为了测试把两个int相加作为哈希码

view plaincopy to clipboardprint? 
class Class3   
   {   
       public int x;   
       int y;   
       public Class3(int x, int y)   
       {   
           this.x = x;   
           this.y = y;   
       }   
       public override int GetHashCode()   
       {   
           Console.WriteLine("判断hashcode");   
           return x + y;   
       }   
       public override bool Equals(object obj)   
       {   
           Console.WriteLine("判断equals");   
           return base.Equals(obj);   
       }   
       public override string ToString()   
       {   
           return x.ToString() + y.ToString();   
       }   
   }  
class Class3 
    { 
        public int x; 
        int y; 
        public Class3(int x, int y) 
        { 
            this.x = x; 
            this.y = y; 
        } 
        public override int GetHashCode() 
        { 
            Console.WriteLine("判断hashcode"); 
            return x + y; 
        } 
        public override bool Equals(object obj) 
        { 
            Console.WriteLine("判断equals"); 
            return base.Equals(obj); 
        } 
        public override string ToString() 
        { 
            return x.ToString() + y.ToString(); 
        } 
    }

接着在控制台程序里测试下,

在插入了cc之后把cc的状态更改了,再通过哈希表中cc那项的key访问值则什么也不会输出,因为在哈希表中找不到cc的哈希码对应的项

正确的做法是把cc那项remove掉再重新插入新的cc项。

view plaincopy to clipboardprint? 
Hashtable ht = new Hashtable();   
Class3 cc = new Class3(2, 3);   
Class3 cc2 = new Class3(1, 4);   
Class3 cc3 = new Class3(3, 3);   
ht.Add(cc, "test1");   
ht.Add(cc2, "test2");   
ht.Add(cc3, "test3");    
//cc.x = 5;   
foreach (var item in ht.Keys)   
{   
  Console.WriteLine(item.ToString());   
  Console.WriteLine(ht[item]);   
}   
Console.Read();  
Hashtable ht = new Hashtable(); 
Class3 cc = new Class3(2, 3); 
Class3 cc2 = new Class3(1, 4); 
Class3 cc3 = new Class3(3, 3); 
ht.Add(cc, "test1"); 
ht.Add(cc2, "test2"); 
ht.Add(cc3, "test3"); 
//cc.x = 5; 
foreach (var item in ht.Keys) 

   Console.WriteLine(item.ToString()); 
   Console.WriteLine(ht[item]); 
}

Console.Read();

下面是输出的情况

当向Hashtable里插入cc时,这时候哈希表是空的,所以只需要判断hashcode就行了;

当cc2插入哈希表时,判断哈希码,由于已经有了一个相同的哈希码的项,所以这时候需要判断它俩是不是相同的对象,不相同再插入哈希表。

输出的时候,

注意cc2那一项判断了两次Equals,这是由于按照哈希码查找到两个项,再通过key的判断取出这项的值(key是按照插入的先后顺序排列,对比的时候也是从前向后)。

从上面的讨论可以看出,哈希码只有在作为集合索引的时候才起作用,平时根本用不上这个功能,GetHashCode方法其实不用放在Object里面,而是可以单独放在一个接口里面。

















本文转自cnn23711151CTO博客,原文链接:http://blog.51cto.com/cnn237111/588684  ,如需转载请自行联系原作者