理解Java中的hashCode和equals 方法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解Java中的hashCode和equals 方法相关的知识,希望对你有一定的参考价值。

在Java里面所有的类都直接或者间接的继承了java.lang.Object类,Object类里面提供了11个方法,如下: 

Java代码  技术分享图片

  1. ````  

  2. 1,clone()  

  3. 2,equals(Object obj)  

  4. 3,finalize()  

  5. 4,getClass()  

  6. 5,hashCode()  

  7. 6,notify()  

  8. 7,notifyAll()  

  9. 8,toString()  

  10. 9,wait()  

  11. 10,wait(long timeout)  

  12. 11,wait(long timeout, int nanos)  

  13. ````  



这里面我们常用的方法有三个: 

Java代码  技术分享图片

  1. ````  

  2. toString()  

  3. equals(Object obj)  

  4. hashCode()  

  5. ````  




toString方法,相信用过Java的人都不会陌生,默认打印的是:类名@十六进制的hashCode,源码中定义如下: 

Java代码  技术分享图片

  1. ````  

  2.     public String toString() {  

  3.         return getClass().getName() + "@" + Integer.toHexString(hashCode());  

  4.     }  

  5. ````  

在经过重写后,我们可以打印一个class的所有属性,这样在打印log或调试时比较方便。 

下面重点介绍下hashCode和equals方法: 



(1)equals方法,在JDK默认的情况下比较的是对象的内存地址,源码如下: 

Java代码  技术分享图片

  1. ````  

  2.     public boolean equals(Object obj) {  

  3.         return (this == obj);  

  4.     }  

  5. ````  



(2)hashcode方法,默认情况下返回的是一个唯一的整数,代表该实例的内存地址,注意这个数字 
并不是实际的内存地址,Java是没办法直接获取内存地址的,必须得由C或者C++获取,所以这个方法是用 
native修饰的 

Java代码  技术分享图片

  1. ````  

  2. public native int hashCode();  

  3. ````  





由于默认情况下,equals方法比较的是内存地址,而在实际开发中,我们判断两个对象是否相等,一般都是根据对象的属性来判断的, 
所以需要重写这个方法,不然的话,是没办法比较的。举例如下: 


定义的类如下: 

Java代码  技术分享图片

  1. ````  

  2. public class Hero {  

  3.     private String id;  

  4.     private String name;  

  5.   

  6.     public Hero() {  

  7.     }  

  8.   

  9.     public Hero(String id, String name) {  

  10.         this.id = id;  

  11.         this.name = name;  

  12.     }  

  13.   

  14.     public String getId() {  

  15.         return id;  

  16.     }  

  17.   

  18.     public void setId(String id) {  

  19.         this.id = id;  

  20.     }  

  21.   

  22.     public String getName() {  

  23.         return name;  

  24.     }  

  25.   

  26.     public void setName(String name) {  

  27.         this.name = name;  

  28.     }  

  29.   

  30.   

  31. }  

  32. ````  

直接比较两个对象,结果是不相等的: 

Java代码  技术分享图片

  1. ````  

  2.  ``````  Hero h1=new Hero("1","张飞");  

  3.   

  4.         Hero h2=new Hero("1","张飞");  

  5.   

  6.         //false  

  7.         System.out.println(h1.equals(h2));  

  8. ````  




因为他们的内存地址是不同的,所以结果是false,如果我们想要认为他是相等的,那么就需要重写 
equals方法: 

Java代码  技术分享图片

  1. ````  

  2.     @Override  

  3.     public boolean equals(Object o) {  

  4.         if (this == o) return true;//如果内存地址相等,则两个对象必定相等  

  5.         if (o == null || getClass() != o.getClass()) return false;//如果有一个为null或者class不一样,认为必不相等  

  6.   

  7.         Hero hero = (Hero) o;//  

  8.   

  9.         if (id != null ? !id.equals(hero.id) : hero.id != nullreturn false;//比较id,不相等就返回false  

  10.         return name != null ? name.equals(hero.name) : hero.name == null;//上面的条件通过后比较name,如果相等返回true,不相等返回false  

  11.   

  12.     }  

  13.   

  14. ````  



在重写equals方法后,我们在比较两个对象,发现就相等了 

Java代码  技术分享图片

  1. ````  

  2. ```     Hero h1=new Hero("1","张飞");  

  3.   

  4.         Hero h2=new Hero("1","张飞");  

  5.   

  6.         //true  

  7.         System.out.println(h1.equals(h2));  

  8. ````  



接着我们看第二个例子,将其放入ArrayList中,然后判断是否存在,发现也生效了: 

Java代码  技术分享图片

  1. ````  

  2. `        Hero h1=new Hero("1","张飞");  

  3.   

  4.         List<Hero> heros=new ArrayList<Hero>();  

  5.   

  6.         heros.add(h1);  

  7.   

  8.         //true  

  9.         System.out.println(heros.contains(new Hero("1","张飞")));  

  10. ````  



到目前为止,我们还没有对hashCode进行操作,那么大家可能会有一个疑问,既然都有equals方法比较了,为啥还需要hashCode方法呢? 别着急,继续看下面的例子: 

我们都知道在Java里面HashSet类,去无序去重的,下面看一下,只重写equasl方法能不能实现对class的去重: 

Java代码  技术分享图片

  1. ````  

  2.   

  3. `        Hero h1=new Hero("1","张飞");  

  4.   

  5.         Hero h2=new Hero("1","张飞");  

  6.   

  7.         Set<Hero> heros=new HashSet<Hero>();  

  8.         heros.add(h1);  

  9.         heros.add(h2);  

  10.   

  11.         //2  

  12.         System.out.println(heros.size());  

  13.         //false  

  14.         System.out.println(heros.contains(new Hero("1","张飞")));  

  15. ````  




从上面的结果看,并没有去重,有的小伙伴会说为啥时string类型的时候就能去重?这是因为Stirng类默认已经重写了equals和hashcode方法,当然所有的基本类型都重写这两个方法了。 

接着回到上面的问题,为什么在HashSet中去重失效了呢? 


其实,不止是HashSet,在HashMap和Hashtable等等所有使用hash相关的数据结构中,如果使用时不重写hashcode,那么就没法比较对象是否存在。 


这其实与HashMap存储原理相关(HashSet底层用的也是HashMap),HashMap在存储时其实是采用了数组+链表的存储结构,数组 
中的每一个元素,我们可以理解成是一个buckets(桶),桶里面的结构是链表,而数据是如何分到各个桶里面其实与hashCode有很大关系,只有hashCode一样的 
对象才能被分到一个桶里。存的时候,遍历链表判断是否存在,如果存在就不覆盖,如果不存在就把该元素放在链表的头,把next指向上一次的头,而读取的时候先定位到桶里,然后遍历 
链表找到该元素即可。 



理解了这些,就明白了为啥上面的例子中,去重失效了。就是因为他们的hashCode不一样,导致被分到不同的桶里面了,自然就没法去重了。 

重写hashCode之后,再看结果: 

Java代码  技术分享图片

  1. ````  

  2.     @Override  

  3.     public int hashCode() {  

  4.         return  Integer.parseInt(id);  

  5.     }  

  6. ````  



Java代码  技术分享图片

  1. ````  

  2. ·        Hero h1=new Hero("1","张飞");  

  3.   

  4.         Hero h2=new Hero("1","张飞");  

  5.   

  6.         Set<Hero> heros=new HashSet<Hero>();  

  7.         heros.add(h1);  

  8.         heros.add(h2);  

  9.   

  10.         //1  

  11.         System.out.println(heros.size());  

  12.         //true  

  13.         System.out.println(heros.contains(new Hero("1","张飞")));  

  14. ````  



这下结果就对了。 


那么问题来了,为啥需要hashCode? 因为在HashSet中,可以存储大量的元素,如果没有hashCode,那么每次就得全量的比较每一个元素,来判断 
是否存在,这样以来效率肯定极低,而有了hashCode之后,只需要找到该数据的链表,然后遍历这个链表的数据即可,这样以来效率 
就大大提升。 

一个Java交流平台分享给你们,让你在实践中积累经验掌握原理。如果你想拿高薪,想突破瓶颈,想跟别人竞争能取得优势的,想进BAT但是有担心面试不过的,可以加Java学习交流群:642830685


注:加群要求


1、大学学习的是Java相关专业,毕业后面试受挫,找不到对口工作


2、在公司待久了,现在过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的


3、参加过线下培训后,知识点掌握不够深刻,就业困难,想继续深造


4、已经在Java相关部门上班的在职人员,对自身职业规划不清晰,混日子的


5、有一定的C语言基础,接触过java开发,想转行的



总结: 

(1)如果两个对象相等,那么他们必定有相同的hashcode 

(2)如果两个对象的hashcode相等,他们却不一定相等 

(3)重写equasl方法时,一定要记得重写hashcode方法,尤其用在hash类的数据结构中。 


以上是关于理解Java中的hashCode和equals 方法的主要内容,如果未能解决你的问题,请参考以下文章

java中“==”和equals()以及hashCode()的理解

java中“==”和equals()以及hashCode()的理解

在java中,关于equals(),和hashCode()的重写问题。

面试点:Java 中 hashCode() 和 equals() 的关系

equals,hashCode 方法理解

【彻底理解】 为啥重写equals()方法为啥要重写hashCode()方法