根据Object.hashCode的通用约定:
举个例子:下图的hash算法为 hashcode = num % 4;
图1
上图的两个对象相同(意思就是同一个对象),hashcode的值都是1.
图2
上图的两个对象的hashcode的值都是1,但是一个对象是13,一个对象是17,它们发生了hash冲突。
比如在HashSet集合里,它的元素是无序且不可重复的。所以在添加一个元素前,就需要判断集合里是不是已经有了这个元素。
那么该怎么判断呢?
如果用equals方法,那就得遍历整个集合去看一一比较有没有重复,想添加一个元素就需要O(n)的时间复杂度。
如果用hashcode方法,我只需计算出hash值,然后根据hash值去查找是否已经有这个元素了,只需要O(1)的时间复杂度。
所以通常是先执行hashcode方法,如果一样,再执行equals方法。
在Object类里,equals方法和hashcode方法都是比较对象的内存地址。但是在HashSet里,我们不想根据内存地址去判断是否相等,想根据key值去判断是否相等,所以我们需要重写。
接下来会有4个实验:
实验1:两个方法都不重写。
实验2:重写equals方法,不重写hashcode方法。
实验3:重写hashcode方法,不重写equals方法。
实验4:重写两个方法。
现在我们有两个人:
Person p1 = new Person("张三");
Person p2 = new Person("张三");
按照“先执行hashcode方法,如果一样,再执行equals方法”的逻辑,推出以下结果:
实验1:集合里会有两个张三。
实验2:集合里会有两个张三。
实验3:集合里会有两个张三。
实验4:集合里只有1个张三,达到了HashSet不重复的目的。
下面做的实验结果和这里的推论一样,不想看可以直接跳到结尾。
创建一个Person类:
public class Person { private String name; public Person(String name) { this.name = name; } public String getName() { return name; } @Override public String toString() { return name; } //按照实验要求选择性注释 /*@Override public boolean equals(Object obj) { if(obj == null) { return false; } if(obj.getClass() != obj.getClass()){ return false; } return ((Person)obj).getName() == getName(); }*/ //按照实验要求选择性注释 /*@Override public int hashCode() { return getName().hashCode(); }*/ }
再来个测试类:
import java.lang.*; import java.util.HashSet; import java.util.Set; public class Main { public static void main(String[] args) { Set<Person> set = new HashSet<>(); Person p1 = new Person("张三"); Person p2 = new Person("张三"); set.add(p1); set.add(p2); System.out.println(set); } }
实验1:两个方法都不重写。
结果:
实验2:重写equals方法,不重写hashcode方法。
结果:
实验3:重写hashcode方法,不重写equals方法。
结果:
实验4:重写两个方法。
结果:
Q1:什么场景下,需要重写equals方法和hashcode方法?
A1:判断两个对象是否相等,不是根据对象的内存地址去判断,而是根据key去判断的时候,就需要重写。
比如HashSet集合。
Q2:为什么重写equals方法,还必须要重写hashcode方法?
A2:因为HashSet的add方法是先执行hashcode方法,如果一样,再执行equals方法。
为了保证性能必须先执行hashcode方法,所以需要重写hashcode方法。
Q3:那我只重写hashcode方法,不重写equals方法不可以吗?
A3:不可以哦。一是hashcode方法会有极小概率产生hash冲突(两个对象的hashcode值一样),它只是用来判断该对象在hash表中的位置的,真正判断两个对象是否相等还得用equals方法。
二是不同业务需求不一样,有些需求需要根据姓名或ID啥的key值去判断是否相等,如果不重写equals方法,那就会默认地根据内存去判断。
为什么重写equals方法,还必须要重写hashcode方法
原文:https://www.cnblogs.com/ledphz/p/11748441.html