一、背景
1.一般的话面试官都很喜欢问hashcode与equals的区别是什么,这里先告诉你他们没有联系的。但是为什么会把他们混为一谈呢,下面我就按照我自己的理解告诉你们。
二、总结
1.我大概总结下,像Hash开头的类,HashMap、HashSet、Hash等等,哈希代码值可以提高性能,实现了hashCode一定要实现equals,因为底层HashMap就是通过这2个方法判断重复对象的。先通过hashcode来比较,如果hashcode相等,那么就用equals方法来比较两个对象是否相等,用个例子说明:如果hash表中的8个位置,就好比8个桶,每个桶里能装很多的对象,对象A通过hash函数算法得到将它放到1号桶中,当然肯定有别的对象也会放到1号桶中,如果对象B也通过算法分到了1号桶,那么它如何识别桶中其他对象是否和它一样呢,这时候就需要equals方法来进行筛选了。
2.如果两对象equals()是true,那么它们的hashCode()值一定相等。
3.如果两对象的hashCode()值相等,它们的equals不一定相等。
三、什么是hashcode?
1.hash和hash表
Hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。(百度百科)。
hash是一个函数,该函数中的实现就是一种算法,就是通过一系列的算法来得到一个hash值,这个时候,我们就需要知道另一个东西,hash表,通过hash算法得到的hash值就在这张hash表中,也就是说,hash表就是所有的hash值组成的,有很多种hash函数,也就代表着有很多种算法得到hash值,如上面截图的三种。
2.hashcode
hashcode就是通过hash函数得来的,通俗的说,就是通过某一种算法得到的,hashcode就是在hash表中有对应的位置。每个对象都有hashcode,对象的hashcode怎么得来的呢?首先一个对象肯定有物理地址,在别的博文中会hashcode说成是代表对象的地址,这里肯定会让读者形成误区,对象的物理地址跟这个hashcode地址不一样,hashcode代表对象的地址说的是对象在hash表中的位置,物理地址说的对象存放在内存中的地址,那么对象如何得到hashcode呢?通过对象的内部地址(也就是物理地址)转换成一个整数,然后该整数通过hash函数的算法就得到了hashcode,所以,hashcode是什么呢?就是在hash表中对应的位置。这里如果还不是很清楚的话,举个例子,hash表中有 hashcode为1、hashcode为2、(...)3、4、5、6、7、8这样八个位置,有一个对象A,A的物理地址转换为一个整数17(这是假如),就通过直接取余算法,17%8=1,那么A的hashcode就为1,且A就在hash表中1的位置。肯定会有其他疑问,接着看下面,这里只是举个例子来让你们知道什么是hashcode的意义。
四、hashcode的作用
前面说了这么多关于hash函数,和hashcode是怎么得来的,还有hashcode对应的是hash表中的位置,可能大家就有疑问,为什么hashcode不直接写物理地址呢,还要另外用一张hash表来代表对象的地址?接下来就告诉你hashcode的作用,
1、HashCode的存在主要是为了查找的快捷性,HashCode是用来在散列存储结构中确定对象的存储地址的(后半句说的用hashcode来代表对象就是在hash表中的位置),为什么hashcode就查找的更快,比如:我们有一个能存放1000个数这样大的内存中,在其中要存放1000个不一样的数字,用最笨的方法,就是存一个数字,就遍历一遍,看有没有相同得数,当存了900个数字,开始存901个数字的时候,就需要跟900个数字进行对比,这样就很麻烦,很是消耗时间,用hashcode来记录对象的位置,来看一下。hash表中有1、2、3、4、5、6、7、8个位置,存第一个数,hashcode为1,该数就放在hash表中1的位置,存到100个数字,hash表中8个位置会有很多数字了,1中可能有20个数字,存101个数字时,他先查hashcode值对应的位置,假设为1,那么就有20个数字和他的hashcode相同,他只需要跟这20个数字相比较(equals),如果每一个相同,那么就放在1这个位置,这样比较的次数就少了很多,实际上hash表中有很多位置,这里只是举例只有8个,所以比较的次数会让你觉得也挺多的,实际上,如果hash表很大,那么比较的次数就很少很少了。 通过对原始方法和使用hashcode方法进行对比,我们就知道了hashcode的作用,并且为什么要使用hashcode了
五、第一个代码例子
1.student类
public class Student {
int age;
String name;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public boolean equals(Object o) {
//Object的equals默认比较,引用地址是否一样
if (this == o) return true;
//null或者判断是否类型相同
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
//利用jdk1.7工具Objects
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
@Override
public String toString() {
return name+"-"+age;
}
}
2.测试类
public class demo1 {
public static void main(String[] args) {
//新建Student
Student p1 = new Student(10,"xiao");
Student p2 = new Student(10,"xiao");
Student p3 = new Student(10,"da");
Student p4 = new Student(10,"zhong");
//HashSet底层去重就是通过对象的equals和hashcode来判断去重的
//准确讲是HashMap的key去重
HashSet set = new HashSet();
set.add(p1);
set.add(p2);
set.add(p3);
set.add(p4);
System.out.println("1)、如果两个对象相等equals,那么他们的hashcode()值一定相同");
System.out.printf("p1.equals(p2):%s;(hashCode值)p1==p2:%s\n",p1.equals(p2),p1.hashCode() == p2.hashCode());
System.out.println("2)、如果两个对象hashcode()相等,那么他们equals值不一定相同(哈希冲突)");
System.out.printf("p1.equals(p4):%s;(hashCode值)p1==p4:%s\n",p1.equals(p4),p1.hashCode() == p4.hashCode());
System.out.printf("p3(%d)\n",p3.hashCode());
System.out.printf("set:%s\n",set);
}
}
3.结果(完成了去重)
1)、如果两个对象相等equals,那么他们的hashcode()值一定相同
p1.equals(p2):true;(hashCode值)p1==p2:true
2)、如果两个对象hashcode()相等,那么他们equals值不一定相同(哈希冲突)
p1.equals(p4):false;(hashCode值)p1==p4:false
p3(4468)
set:[da-10, zhong-10, xiao-10]
4.总结:
如果缺少hashCode或equals方法。
输出结果(不会去重)。
六、第二个例子
1.A类
public class A {
private int a=1;
private int b=2;
@Override
public boolean equals(Object o) {
//传入的对象o是否是A的实例
if (o instanceof A){
A a1 = (A) o;
if (this.a==a1.a&&this.b==a1.b){
return true;
}
}
return false;
}
public static void main(String[] args) {
A a1 = new A();
A a2 = new A();
System.out.println(a1.hashCode());
System.out.println(a2.hashCode());
System.out.println(a1.equals(a2));
}
}
2.结果
664740647
804564176
true
3.分析
比如:有个A类重写了equals方法,但是没有重写hashCode方法,看输出结果,对象a1和对象a2使用equals方法相等,按照上面的hashcode的用法,那么他们两个的hashcode肯定相等,但是这里由于没重写hashcode方法,他们两个hashcode并不一样,所以,我们在重写了equals方法后,尽量也重写了hashcode方法,通过一定的算法,使他们在equals相等时,也会有相同的hashcode值。
七、总结
- hashCode() 在散列表中才有用,在其它情况下没用
- 哈希值冲突了场景,hashCode相等,但equals不等
八、结束
参考博客:https://www.cnblogs.com/whgk/p/6071617.html
Always keep the faith!!!