平时听到最多的就是重写equals方法就必须要重写hashcode方法,那到底是为什么呢?本着学习认真的态度,我就带着小伙伴们一起学习一下~
首先要了解的是equals()是Object类的方法。我们来看一下在Object类中equals是怎么被描述的:
equals位置
首先我们知道Object是所有类的父类也就是超类,我们看一下在Object中equals方法的实现是直接比较的是地址。我们知道基本类型和String都重写了equals方法。那我们先看一下我们经常使用的String类是怎么对equals方法进行重写的:
String重写equals
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
我简单分析一下:
1、首先判断地址是不是相等,如果地址相等那肯定是同一个字符串,直接返回true
2、如果地址不相等,判断一下是不是字符串类型,如果连字符串类型都不是也就没有比较的必要,直接返回false
3、如果是字符串类型就判断一下两个字符串的长度是不是相等,如果两个字符串的长度都不想等那肯定字符串的内容是不相等的
4、如果字符串的长度相等,再进行判断,一个字符一个字符的进行比较,期间发现只要有不相等就返回false
5、以上步骤执行完没有发现有不相等的字符,那两个字符串的内容也就相等了
String重写hashcode方法
以上就是我通过阅读源码,以我个人的理解来解释的,然后我们再看一下Integer类是如何重写equals的
Integer重写equals
相比于String,Integer就简单很多了。
1、先判断是不是Integer类型,类型不同直接返回false
2、类型相同把Integer转换为int类型然后进行数值大小的比较
到现在我们好像还是不知道为什么重写了equals方法就一定要重写hashcode方法,我们从一个集合类入手就是hashset,对集合有一定了解的同学都知道hashset可以保证不存放相同的元素,那hashset是怎么判读两个对象相等的呢,我们走进去看一下hashset的源码
hashset源码
构造函数
add方法
put方法
putVal方法
这个方法代码有些长,我只截取了部分。比较重要的两部分代码
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
计算数组下标,并对null做处理 ,(n - 1) & hash 确定元素存放在哪个桶中,如果桶为空,新生成结点放入桶中
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
先比较hash值,这里提一下&&是一个短路操作,如果p.hash == hash是false,那就不用进行后面的比较了,小伙伴们都知道,如果hashcode相等,两个对象不一定相等,如果hashcode不等,两个对象一定不等,也就是说如果hashcode都不一样就没必要进行后面的比较了,一定是两个不同的对象,直接添加就ok了。
看一下hashcode的源码
调用的是本地方法,因为java的内存是安全的,所以不能根据散列码就直接得到对象的内存地址,但实际上,hashcode是根据对象的内存地址经哈希算法得来的。
所以现在我们大概知道了为什么重写equals方法的同时也要重写hashcode方法。在判断对象相等之前我们先对两个对象的hashcode做比较,如果hashcode值不相等那一定是两个不相等的对象,如果hashcode相等,那么有可能是同一个对象,也有可能是不同的对象,我们知道对象的hashcode值是根据hash算法散列在hash表上的,所以有一定的概率会产生hash冲突,所以我们判断两个对象的hasncode相等之后再进行equals判断,这样会省去一些比较的步骤。
Object中的equals方法比较的是两个对象是否具有相同的引用,它们一定是相等的。从这点上看,将其作为默认操作也是合乎情理的。然而,对于多数类来说, 这种判断并没有什么意义。 例如, 采用这种方式比较两个类的实例对象是否相等就完全没有意义。然而, 经常需要检测两个对象状态的相等性, 如果两个对象的状态相等, 就认为这两个对象是相等的。例如, 如果两个雇员对象的姓名、 薪水和雇佣日期都一样, 就认为它们是相等的(在实际的雇员数据库中,比较 ID 更有意义。)
以上就是我个人的理解,文章中有什么地方错了,希望小伙伴们在评论区中指正~~