一、Hash简介
什么是Hash?音译成中文就是散列的意思。就是把任意长度的输入,通过散列算法变换成固定长度的输出,该输出就是散列值。这里有一个值得注意的地方,那就是输入不同经过Hash函数可能相同的Hash值(碰撞);如果两个Hash值不同,那么这两个Hash值对应的原始输入必不同。
二、equals()和hashcode()方法
JDK中对equals(Object obj)和hashcode()这两个方法的定义和规范是:Java中任何一个对象都具有这两个方法,因为它们是在Object类中定义的。equals(Object obj)方法用来判断两个对象是否“相同”,如果“相同”则返回true,否则返回false;Java默认的实现是若两个对象指向同一内存地址则认为相同,否则认为不同。 hashCode()方法返回一个int数,在Object类中的默认实现是“将该对象的内部地址转换成一个整数返回”。
在Java中,HashSet是基于HashMap底层实现的一个集合,它具有快速存取的特征。当集合要添加新的对象时,先调用这个对象的 hashCode方法,得到对应的hashcode值。实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode 值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在前面提到的“冲突”问题。这样一来实际调用 equals方法的次数就大大降低了。说通俗一点:Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的 字段等)映射成一个数值(通过调用hashCode方法获得),这个数值称作为散列值。
equals和hashCode这两个方法的归纳总结如下:
1.若重写了equals(Object obj)方法,则有必要重写hashCode()方法;
2.若两个对象equals(Object obj)返回true,则hashCode()也要返回相同的int数;
3.若两个对象equals(Object obj)返回false,则hashCode()不一定返回不同的int数;
4.若两个对象hashCode()返回相同int数,则equals(Object obj)不一定返回true;
5.若两个对象hashCode()返回不同int数,则equals(Object obj)一定返回false;
6.同一对象在执行期间若已经存储在集合中,则不能修改影响hashCode值的相关信息,否则会导致内存泄露问题。
三、什么时候重写equals()和hashCode()
一般来说涉及到对象存储就要重写equals()方法,基本原则是只要重写了equals()就要重写hashCode()。为什么要这样?首先看一下一个对象放入HashSet的流程图:
如果重写了equals()但没有重写hashCode(),就有可能出现这样一种情况:有两个不同的对象,它们的所有字段值均相等,这时equals()判断两个对象相等。但是由于此时hashCode()还是默认的hashCode(),它们地址不同,所以它们的hashCode()不同。这样会带来什么影响?简单地说,两个Point类对象p1,p2都为(1,1),依次把它们添加到同一个HashSet中,它们由于hashcode()不同都会添加成功。但是这违背了HashSet集合中不可重复性这一原则。
所以综上所述,若一个自定义的类重写了equals(),一定要记得重写hashCode()。
四、怎么重写equals()和hashCode()
在自定义类中重写equals()的原则一般是:首先判断两个对象是不是同一类的对象,如果不是直接返回false;这里要注意一下:
if((obj == null) || (obj.getClass() != this.getClass()))
return false; //应该这么写
if(!(obj instanceof Test))
return false; // 避免这么写
因为 :
dog instanceof Animal 得true;
animal instanceof Dog 得false;
这样就可能出现:
animal.equals(dog)得true;
dog.equals(animal)得false。
然后判断两个对象的所有字段,如果两个对象的所有字段均相等,则认为equals()返回true,否则返回false。
再看看hashCode()的重写原则,上面提到如果2个对象通过equals调用后返回是true,那么这个2个对象的hashCode方法也必须返回同样的int型散列码;如果2个对象通过equals返回false,他们的hashCode返回的值允许相同。所以说,参与equals函数的字段,也必须都参与hashCode 的计算。然后程序员根据自己选择的散列函数重写hashCode()方法。常见的重写方法是:
int hash = 7;
hash = 31 * hash + 字段1贡献分量;
hash = 31 * hash + 字段2贡献分量;
.....
return hash;
参考链接:
https://www.cnblogs.com/lulipro/p/5628750.html
https://blog.csdn.net/haobaworenle/article/details/53819838
https://blog.csdn.net/zhangyuan19880606/article/details/51240372