如何重写GetHashCode方法

分析问题

  GetHashCode方法的功能是根据当前对象返回一个散列值,可以用来在数据结构算法或哈希算法中使用。GetHashCode算法的根本要求是同一个类型对象调用GetHashCode必须返回相同的值,更进一步的,相等的对象必须返回相同的值,这样才能使得这个散列值可以用在容器算法中。

  GetHashCode方法在Object类型有一个默认实现,基本的思想是使用一个内部对象索引成员来生成散列值。对象索引永远不会重复,也不会被改变,但同一对象只拥有一个索引,这保证了GetHashCode最基本的需求:同一对象返回相同的值。事实上,对于所有值类型来说GetHashCode的默认实现是永远不满足这一引申需求的。

说明

  对象的相等是指调用Equals方法返回true。这也是之前代码编译会产生一个警告。Equals方法被重写了,编译器无法确定原先被认为相等的、将产生相同HashCode的对象是否在新的比较方法中返回true。

  在自定义的类型需要作为哈希表的键值使用时,就需要重写GetHashCode方法,尤其是当新类型是值类型或者新类型重写了Equals方法时。在本书前面章节已经提到,重写GetHashCode有三个重要的原则:

  1、相等的对象调用GetHashCode返回相等的值。

  2、同一对象任何时候调用GetHashCode方法返回同一值。

  3、对于所有对象输出值尽量散列分布。

  在Object中实现GetHashCode算法,不能保证上述3条的第一条原则,这是因为在原始基类中,无法获得足够的具体类型的信息,并且不能保证Equals方法不被重写。以下代码是一个重写GetHashCode的示例。

using System;
namespace Test
{
    class OverrideGetHashCode
    {
        public int _MyInt;
        public readonly string _MyString;
        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }
            if (object.ReferenceEquals(this, obj))
            {
                return true;
            }
            if (this.GetType()!=obj.GetType())
            {
                return false;
            }
            OverrideGetHashCode current = obj as OverrideGetHashCode;
            if (_MyInt==current._MyInt&&_MyString==current._MyString)
            {
                return true;
            }
            return false;
        }

        public OverrideGetHashCode(int i, string s)
        {
            _MyInt = i;
            _MyString = s;
        }
        //重写GetHashCode方法
        public override int GetHashCode()
        {
            //依靠只读成员的HashCode
            return _MyString.GetHashCode();
        }

        static void Main()
        {
            //验证相等的对象返回相等的HashCode
            int i = 10;
            string s = "我是字符串";
            OverrideGetHashCode o1 = new OverrideGetHashCode(i, s);
            OverrideGetHashCode o2 = new OverrideGetHashCode(i, s);
            Console.WriteLine("对象是否相等:{0}", (o1.Equals(o2)).ToString());
            Console.WriteLine("相等对象HashCode是否相等:{0}", (o1.GetHashCode() == o2.GetHashCode()).ToString());
            //验证同一对象返回相等的HashCode
            int code = o1.GetHashCode();
            o1._MyInt = 12;
            Console.WriteLine("同一对象HashCode是否相等:{0}", (o1.GetHashCode() == code).ToString());
            Console.Read();
        }
    }
}

  在以上代码中,类型OverrideGetHashCode重写了Equals方法,实现了内容比较,并且重写了GetHashCode方法,在Main方法中,代码运用重写GetHashCode的前两条原则对算法进行了验证,而对于HashCode的散列分布情况,以上代码的算法并不理想,因为具有不同_MyInt值但具有相同_MyString值的对象将拥有相同的HashCode。在以上代码中,有几点需要读者特别留意:

  1、新的GetHashCode算法依赖的是只读成员的GetHashCode算法,这是为了确保第二条原则。

  2、算法不能确保不同对象返回不同的HashCode,这并不完全违反GetHashCode的原则,但却使得该类型在应用在散列表中时拥有较差的性能。

  3、并不是所有类型都会有只读成员,所以通常情况下重写GetHashCode会非常困难。无论如何,确保算法的正确性比确保其效率更为重要。

答案

  Object中GetHashCode的算法保证了同一对象返回同一HashCode,而不同对象则返回不同的HashCode,但对于值类型等视内容相等的对象为相等对象的类型时,默认的GetHashCode算法并不正确。

  重写GetHashCode必须保证同一对象无论何时都返回同一HashCode值,而相等的对象也必须返回相同的值。并且在此基础上,保证HashCode尽量随机地散列分布。

如何重写GetHashCode方法

时间: 2024-10-08 05:56:00

如何重写GetHashCode方法的相关文章

编写高质量代码改善C#程序的157个建议——建议12: 重写Equals时也要重写GetHashCode

建议12: 重写Equals时也要重写GetHashCode 除非考虑到自定义类型会被用作基于散列的集合的键值:否则,不建议重写Equals方法,因为这会带来一系列的问题. 如果编译上一个建议中的Person这个类型,编译器会提示这样一个信息: “重写 Object.Equals(object o)但不重写 Object.GetHashCode()” 如果重写Equals方法的时候不重写GetHashCode方法,在使用如FCL中的Dictionary类时,可能隐含一些潜在的Bug.还是针对上一

【转】重写Equals为什么要同时重写GetHashCode

.NET程序员都知道,如果我们重写一个类的Equals方法而没有重写GetHashCode,则VS会提示警告 :"***"重写 Object.Equals(object o)但不重写 Object.GetHashCode() . 但是,为什么重写Equals一定要同时重写GetHashCode呢? 微软的解释是: GetHashCode 基于适合哈希算法和诸如哈希表的数据结构的当前实例返回一个值. 两个相等的同类型对象必须返回相同的哈希代码,才能确保以下类型的实例正确运行: HashT

【转载】聊一聊C#的Equals()和GetHashCode()方法

首先先谈一下Equals()这个方法: Equals()方法,来自于Object,是我们经常需要重写的方法.此方法的默认实现大概是这样的: public virtual bool Equals(object obj) { if(obj==null) return false; if(GetType() != obj.GetType()) return false; Return true; } 由此可以看出,默认的实现其实比较的是两个对象的内存地址(==操作符默认比较内存地址).值类型和stri

GetHashCode方法学习

GetHashCode方法我的理解是做两个对象的比较,每个对象,不管是值类型还是应用类型都提供这个基本函数,都可以去重写它.GetHashTable通常用于HashTable.List<>.Dictionary等集合,如果两个集合返回的哈希码相同那么这两个集合不一定是同一个集合(也可能是两个相等的集合),但是如果两个集合返回的哈希码不同这两个集合就一定不是同一个集合(由于GetHashTable返回的哈希码为一个整数类型而且整数类型的容量是有限的,这样可能就会出现不同的对象使用GetHashT

我看C#的Equals()和GetHashCode()方法

首先先谈一下Equals()这个方法: Equals()方法,来自于Object,是我们经常需要重写的方法.此方法的默认实现大概是这样的: 1 public virtual bool Equals(object obj) 2 3 { 4 5 if(obj==null) return false; 6 7 if(GetType() != obj.GetType()) return false; 8 9 Return true; 10 11 } 由此可以看出,默认的实现其实比较的是两个对象的内存地址

关于继承时子类重写父类方法和覆盖父类变量的若干问题

假设,子类重载父类的方法,并将子类的成员覆盖. 创建子类对象实例,将其上转型成父类. 例子1 public class Parent { public void init() { System.out.println("1 init parent"); this.demo(); } public void demo() { System.out.println("2 demo parent"); } } public class Son extends Parent

方法重写和方法重载

重写和重载虽然都有一个共同点是发生在方法之间,但是两者之间没有任何的关系! 方法重载:是指一个类中有多个方法,这些方法的名字一样,但是形参不一样 方法重写:发生在子类和父类之间,当子类继承了父类之后,子类就拥有了父类中定义的方法,当然除了构造器没有继承,子类可以去调用父类的方法,也可以重写,在子类中重写父类的方法,保证方法名一样,形参也要一样吧,这样子类再访问这个方法时就会默认使用这个在子类中被重写的方法,而不是父类中那个被覆盖的方法. 当然,我们如果想在子类中使用父类中被覆盖的方法,我们可以使

java重写equals方法(重点讲解)

为什么equals()方法要重写? 判断两个对象在逻辑上是否相等,如根据类的成员变量来判断两个类的实例是否相等,而继承Object中的equals方法只能判断两个引用变量是否是同一个对象.这样我们往往需要重写equals()方法. 我们向一个没有重复对象的集合中添加元素时,集合中存放的往往是对象,我们需要先判断集合中是否存在已知对象,这样就必须重写equals方法. 怎样重写equals()方法? 重写equals方法的要求: 1.自反性:对于任何非空引用x,x.equals(x)应该返回tru

javabean对象要实现的接口们和要重写的方法们

在使用list集合的时候,什么也不用. 原因:list允许存储重复的元素. 在使用set集合的时候,要重写,equals()方法 和 hashCode() 方法. 愿意:set集合 不允许存放相同的元素,而默认比较的是两个 对象地址的值,这样,两个对象恒不等.所以 我们需要重写equals()方法,当再次存入的时候用以判定两个对象到底是不是相等.如果两个对象相等了,那么两个 对象必然应该用有相同的hash值,所以在重写equals() 方法的同时,也要重写hashCode()方法. 如果要使用C