为什么覆写equals必须要覆写hashCode?

=============================================

原文链接: 为什么覆写equals必须要覆写hashCode? 转载请注明出处!

=============================================

《Effective java》Chapter Three  Item 9:Always override hashCode when you overried equals. (下载链接: Effective_java(英文版)第二版.pdf

 1、 在Object中有两个方法:

public native int hashCode();
public boolean equals(Object obj) {
  return (this == obj);//默认比较引用相等
}

所以任何一个对象都有默认的hashCode()和equals()方法。

2、在hash表中,hashCode()方法是参与取/存值运算的。

  例如: 

//HashTabel
if ((e.hash == hash) && e.key.equals(key)) {
     return (V)e.value;
}
//HashMap
 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
     return e;

由此可见,在根据key取value的时候,key相等的必要条件就是两者hashCode相等。

再说为什么覆写equals必须要覆写hashCode?注意:研究的对象是同一个类的两个不同对象(引用不同)

  先看一段代码:

    @Test
    public void testHashCode(){
        Student stu1 = new Student("JACK");
        Student stu2 = new Student("JACK");
        System.out.println(stu1.equals(stu2));//equal返回true
        System.out.println(stu1.hashCode()+"----"+stu2.hashCode());//hashCode 结果不同。由于Student未覆写hashCode方法,在计算的时候使用的是的Object.hashCode(),计算出来两者的hashCode不一致
        HashMap map = new HashMap();
        map.put(stu1,"123456");
        System.out.println(map.get(stu2));//(逻辑上)本想取出“123456”,但hashMap在取值的时候把两者的hashCode加入比较,因此找不到相等的key值,所以取出结果为null

    }
    class Student {
        private String name;
        public Student(String name){
            this.name = name;
        }
        @Override
        public boolean equals(Object obj) {
            //逻辑上名字相等便认为两者相等并不是把引用相等作为判等条件
            return this.name.equals(((Student)obj).name);
        }
    }

运行结果如下:

true
668849042----434176574null

由这段代码可以看出:在执行map.get(stu2)的时候,程序会获取stu2的hashCode。而Student未覆写hashCode(),所以直接使用父类Object.hashCode()计算hash值,而由于Object.hashCode()返回的stu1和stu2的hash值并不相同,因此,map中未找到stu2对应的key值,所以返回的value便为null。

而示例程序中,本想根据名字相等来判断对象相等获取value值“123456”,可由于调用hashCode()的返回值不同导致最终未取得想要的值。那么解决办法是什么?让stu1.hashCode() == stu2.hashCode()。那么该如何实现? 自定义(覆写)hashCode()方法。

所以如果自定义了equals()方法,且返回true的充要条件不是(this == obj),那么就必须覆写hashCode,且必须保证equals为true的时候两者的hashCode必须相等,否则当该对象作为key值存在hash表中的时候,就无法用逻辑上相等的对象取出该key所对应的value(如示例)。

Object specification:

Object specification [JavaSE6]:
? Whenever it is invoked on the same object more than once during an execution of an application, the hashCode method must consistently return the same integer, provided no information   used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
? If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
? It is not required that if two objects are unequal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results.   However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.

----end

时间: 2024-10-27 16:51:42

为什么覆写equals必须要覆写hashCode?的相关文章

覆写equals方法为什么需要覆写hashCode方法

覆写equals方法必须覆写hashCode方法,是JDK API上反复说明的,不过为什么要这样做呢?这两个方法之间有什么关系呢? void test() { // Person类的实例作为Map的key Map<Person, Object> map = new HashMap<Person, Object>(); map.put(new Person("张三"), new Object()); // Person类的实例作为List的元素 List<P

java覆写equals方法

何时需要重写equals() 当一个类有自己特有的“逻辑相等”概念(不同于对象身份的概念). object规范规定,如果要重写equals(),也要重写hashcode() 如何覆写equals() 覆写equals方法 1  使用instanceof操作符检查“实参是否为正确的类型”. 2  对于类中的每一个“关键域”,检查实参中的域与当前对象中对应的域值. 3. 对于非float和double类型的原语类型域,使用==比较: 4  对于对象引用域,递归调用equals方法: 5  对于flo

8.2.3 覆写 Equals

经过对四种不同类型判等方法的讨论,我们不难发现不管是 Equals 静态方法.Equals 虚方法 抑或==操作符的执行结果,都可能受到覆写 Equals 方法的影响.因此研究对象判等就必须将注意 力集中在自定义类型中如何实现 Equals 方法,以及实现怎样的 Equals 方法.因为,不同的类型, 对于“相等”的理解会有所偏差,你甚至可以在自定义类型中实现一个总是相等的类型,例如: class AlwaysEquals { public override bool Equals(object

Hibernate写hql语句与不写hql语句的区别?

写hql语句与不写hql语句的区别? 写hql语句:书写HQL语句,所有的查询与投影的设计均使用HQL语句完成. 不写hql语句:没有任何查询语句,所有的查询与投影的设计使用面向对象格式完成. 二者选用的时机: 不写hql语句,有时比较方便,在不考虑复杂的查询的情况下,可以使用,此方法把某些Hibernate的方法封装起来,不灵活. 写hql语句,复杂的业务逻辑,建议使用,灵活方便. 以下通过俩种方法的对比来着重介绍一下hibernate不写hql的一种简单用法. 写hql语句: 具有丰富的灵活

C++作业代写,C++代写,代写C++,编程代写(微信leechanx)

C++作业代写,C++代写,代写C++,编程代写(微信leechanx) 概念区分之线程安全VS可重入 可重入:当被多个线程调用的时候,不引用.操作任何共享数据. 线程安全:被多个线程并发调用,最终会产生正确的结果. 可重入->由于绝对不引用任何共享数据->所以肯定是线程安全的. 线程安全->如果引用了共享数据,即使最终结果正确,也不是可重入的. 虽然引用了共享数据,但最终结果是正确的 这种引用了共享数据的线程是怎么保证安全的呢?->同步,唯一手段! 线程不安全如何改为线程安全?

C++代写,C++作业代写,代写C++,编程代写(微信leechanx)

C++代写,C++作业代写,代写C++,编程代写(微信leechanx) 主要的GC算法 三种基本方法:标记清除法.复制收集法.引用计数法 高级方法:分代回收法 一.标记清除法 标记阶段:先从根对象开始,以深度遍历的方式对其可达的对象(A可达对象B的意思是:A引用了B)标记,表明这些对象是存活的: 清除阶段:对未标注的对象进行空间回收,同时将所有已标记的对象清理标记状态,为下次标记做准备. 大致过程: 内存空间状态: 缺点: (1)标记-清除算法的比较大的缺点就是垃圾收集后有可能会造成大量的内存

Python代写,Python作业代写,代写Python,代做Python(微信leechanx)

Python代写,Python作业代写,代写Python,代做Python(微信leechanx) Redis:Cannot assign requested address的解决办法 客户端频繁的连服务器,由于每次连接都在很短的时间内结束,导致很多的TIME_WAIT,以至于用光了可用的端口号,所以新的连接没办法绑定端口,即"Cannot assign requestedaddress".是客户端的问题不是服务器端的问题.通过netstat,的确看到很多TIME_WAIT状态的连接.

当写listview的onItemClick的方法时写Toast的参数context写成this出现can&#39;t resolve method ’make text(OnClickListener,java.lang.String,int)&#39;的错误,原因

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Fruit fruit = fruitList.get(position); //Toast.makeText(this,fruit.getName(),Toast.L

Python代写,Python作业代写,代写Python,代做Python

Python代写,Python作业代写,代写Python,代做Python 我是一线IT企业程序员,目前接各种代码代写业务: 代写C语言.代做C语言.C语言作业代写.C语言代写 代写C++.代做C++.C++作业代写.C++作业代写 代写Python.代做Python.Python作业代写.Python作业代做 代写Java.代做Java.Java作业代写.Java作业代做 代写编程作业.代做编程.编程代写.编程代做 先写代码再给钱,不要任何定金!价钱公道,具体见图,诚信第一!(涉及图形化界面.