您的位置 首页 java

Java重写equals方法时为什么要重写hashCode方法

在我们平时编写Java代码时,重写equals方法时一定要重写hashCode方法,这是为什么呢?

在讨论这个问题前,我们先看下Object类中hashCode方法和equals方法。

hashCode方法:

翻译如下:

equals方法:

翻译如下:

1、hashCode方法的作用

在Java中也一样,hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet、HashMap以及HashTable。

为什么这么说呢?考虑一种情况,当向集合中插入对象时,如何判别在集合中是否已经存在该对象了?

也许大多数人都会想到调用equals方法来逐个进行比较,这个方法确实可行。但是如果集合中已经存在一万条数据或者更多的数据,如果采用equals方法去逐一比较,效率必然是一个问题。

此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题,这样一来实际调用equals方法的次数就大大降低了,说通俗一点:Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。

java.util.HashMap的中put方法的具体实现,先计算key的hash值,从table数组中取出对应节点,如果节点不存在则添加一个节点;如果存在则更新value,返回旧value。

hash方法会调用对象的hashCode()方法:

addEntry方法添加新节点:

new一个Entry实例,next指向原有的Entry实例。也就是新new的Entry实例是该链表的头。

Entry是一个静态内部类,有一个属性next,指向下一个Entry,形成一个链表结构。

2、equals方法和hashCode方法

看下面代码:

 package com.test.hashcode;
import java.util.HashMap;
public class Test {
	
	public static void main(String[] args) {
		Student s1 = new Student("Tom", 12);
		Student s2 = new Student("Tom", 12);
		System.out.println("s1和s2是同一个人吗?"+s1.equals(s2));
		System.out.println("s1 hashCode:"+s1.hashCode());
		System.out.println("s2 hashCode:"+s2.hashCode());		     
		HashMap<Student, Integer> hashMap = new HashMap<Student, Integer>();
		hashMap.put(s1, 1);
		System.out.println(hashMap.get(s2));
	}
}

class Student {
    private String name;
    private int age;
   
    public Student(String name, int age) {
		this.name = name;
		this.age = age;
	}

	@Override
    public boolean equals(Object o) {
       if(this == o) {
    	   return true;
       }
       if(o == null || getClass() != o.getClass()) {
         return false;
       }
       Student s = (Student) o;
       return age == s.age && name.equals(s.name);
    }

}  

输出结果:

 s1和s2是同一个人吗?true
s1 hashCode:1118484607
s2 hashCode:1446427658
null  

我们Student类重写了equals方法,hashCode方法没有重写,s1和s2的姓名和年龄相同,equals方法为true,认为是同一个人。但是s1和s2的hashCode返回不同。

我们看下hashMap的get方法,先获取key的hashCode,由于s1和s2的hashCode不同,所以hashMap.get(s2)得到的是null。

接下来我们重写下Student类的hashCode方法,让equals方法和hashCode方法始终在逻辑上保持一致性。

 @Override
public int hashCode() {
   return name.hashCode()+age*20+17;
}  

重新运行,输出结果如下,s1和s2的hashCode相同了,hashMap.get(s2)得到了1。

 s1和s2是同一个人吗?true
s1 hashCode:84531
s2 hashCode:84531
1  

① 在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。

② 如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。

③ 如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。

如果hashCode方法依赖于对象中易变的数据,用户就要当心了,因为此数据发生变化时,hashCode()方法就会生成一个不同的hash值。看下面例子:

 package com.test.hashcode;

import java.util.HashMap;

public class Test {
	
	public static void main(String[] args) {
		Student s1 = new Student("Tom", 12);
		System.out.println("s1 hashCode:"+s1.hashCode());
		     
		HashMap<Student, Integer> hashMap = new HashMap<Student, Integer>();
		hashMap.put(s1, 1);
		
		s1.setAge(13);
		System.out.println("s1 hashCode:"+s1.hashCode());
		System.out.println(hashMap.get(s1));
	}
}

class Student {
    private String name;
    private int age;
   
    public Student(String name, int age) {
		this.name = name;
		this.age = age;
	}

	@Override
    public boolean equals(Object o) {
       if(this == o) {
    	   return true;
       }
       if(o == null || getClass() != o.getClass()) {
         return false;
       }
       Student s = (Student) o;
       return age == s.age && name.equals(s.name);
    }

	@Override
	public int hashCode() {
	   return name.hashCode()+age*20+17;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
	
}  

输出结果:

 s1 hashCode:84531
s1 hashCode:84551
null  

修改了age属性的值,导致hashCode变化,所以输出为“null”。

因此,在设计hashCode方法和equals方法的时候,如果对象中的数据易变,则最好在equals方法和hashCode方法中不要依赖于该字段。

欢迎小伙伴们关注转发点赞,谢谢~~

文章来源:智云一二三科技

文章标题:Java重写equals方法时为什么要重写hashCode方法

文章地址:https://www.zhihuclub.com/170155.shtml

关于作者: 智云科技

热门文章

网站地图