为啥重写equals方法,一定要重写HashCode方法?

Posted

tags:

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

如果你重载了equals,比如说是基于对象的内容实现的,而保留hashCode的实现不变,那么很可能某两个对象明明是“相等”,而hashCode却不一样。
这样,当你用其中的一个作为键保存到hashMap、hasoTable或hashSet中,再以“相等的”找另一个作为键值去查找他们的时候,则根本找不到。
使用HashMap,如果key是自定义的类,就必须重写hashcode()和equals()。
而对于每一个对象,通过其hashCode()方法可为其生成一个整形值(散列码),该整型值被处理后,将会作为数组下标,存放该对象所对应的Entry(存放该对象及其对应值)。 equals()方法则是在HashMap中插入值或查询时会使用到。当HashMap中插入值或查询值对应的散列码与数组中的散列码相等时,则会通过equals方法比较key值是否相等,所以想以自建对象作为HashMap的key,必须重写该对象继承object的hashCode和equals方法。 2.本来不就有hashcode()和equals()了么?干嘛要重写,直接用原来的不行么? HashMap中,如果要比较key是否相等,要同时使用这两个函数!因为自定义的类的hashcode()方法继承于Object类,其hashcode码为默认的内存地址,这样即便有相同含义的两个对象,比较也是不相等的,例如,生成了两个“羊”对象,正常理解这两个对象应该是相等的,但如果你不重写 hashcode()方法的话,比较是不相等的!
HashMap中的比较key是这样的,先求出key的hashcode(),比较其值是否相等,若相等再比较equals(),若相等则认为他们是相等的。若equals()不相等则认为他们不相等。如果只重写hashcode()不重写equals()方法,当比较equals()时只是看他们是否为同一对象(即进行内存地址的比较),所以必定要两个方法一起重写。HashMap用来判断key是否相等的方法,其实是调用了HashSet判断加入元素是否相等。
引用别人说的一段话哈~
一般来说,如果你要把一个类的对象放入容器中,那么通常要为其重写equals()方法,让他们比较地址值而不是内容值。特别地,如果要把你的类的对象放入散列中,那么还要重写hashCode()方法;要放到有序容器中,还要重写compareTo()方法。
equals()相等的两个对象,hashcode()一定相等;
equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。换句话说,equals()方法不相等的两个对象,hashcode()有可能相等。(我的理解是由于哈希码在生成的时候产生冲突造成的)。
反过来:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等
我的理解哈,
参考技术A 重载了equals,比如说是基于对象的内容实现的,而保留hashCode的实现不变,那么很可能某两个对象明明是“相等”,而hashCode却不一样。
这样,当你用其中的一个作为键保存到hashMap、hasoTable或hashSet中,再以“相等的”找另一个作为键值去查找他们的时候,则根本找不到。
使用HashMap,如果key是自定义的类,就必须重写hashcode()和equals()。
而对于每一个对象,通过其hashCode()方法可为其生成一个整形值(散列码),该整型值被处理后,将会作为数组下标,存放该对象所对应的Entry(存放该对象及其对应值)。 equals()方法则是在HashMap中插入值或查询时会使用到。当HashMap中插入值或查询值对应的散列码与数组中的散列码相等时,则会通过equals方法比较key值是否相等,所以想以自建对象作为HashMap的key,必须重写该对象继承object的hashCode和equals方法。 2.本来不就有hashcode()和equals()了么?干嘛要重写,直接用原来的不行么? HashMap中,如果要比较key是否相等,要同时使用这两个函数!因为自定义的类的hashcode()方法继承于Object类,其hashcode码为默认的内存地址,这样即便有相同含义的两个对象,比较也是不相等的,例如,生成了两个“羊”对象,正常理解这两个对象应该是相等的,但如果你不重写 hashcode()方法的话,比较是不相等的!
HashMap中的比较key是这样的,先求出key的hashcode(),比较其值是否相等,若相等再比较equals(),若相等则认为他们是相等的。若equals()不相等则认为他们不相等。如果只重写hashcode()不重写equals()方法,当比较equals()时只是看他们是否为同一对象(即进行内存地址的比较),所以必定要两个方法一起重写。HashMap用来判断key是否相等的方法,其实是调用了HashSet判断加入元素是否相等。
引用别人说的一段话哈~
一般来说,如果你要把一个类的对象放入容器中,那么通常要为其重写equals()方法,让他们比较地址值而不是内容值。特别地,如果要把你的类的对象放入散列中,那么还要重写hashCode()方法;要放到有序容器中,还要重写compareTo()方法。
equals()相等的两个对象,hashcode()一定相等;
equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。换句话说,equals()方法不相等的两个对象,hashcode()有可能相等。(我的理解是由于哈希码在生成的时候产生冲突造成的)。
反过来:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等
参考技术B

自己的整理,共同学习为什么重写equals()就一定要重写hashCode()方

JAVA中重写equals()方法为什么要重写hashcode()方法说明

重写hashCode()时最重要的原因就是:无论何时,对同一个对象调用hashCode()都应该生成同样的值。如果在将一个对象用put()方法添 加进HashMap时产生一个hashCode()值,而用get()取出时却产生了另外一个 hashCode()值,那么就无法重新取得该对象了。所以,如果你的hashCode()方法依赖于对象中易变的数据,那用户就要小心了,因为此数据发 生变化时,hashCode()就会产生一个不同的hash码,相当于产生了一个不同的“键”。 

      Object的hashCode()方法,返回的是当前对象的内存地址。下次如果我们需要取一个一样的“键”对应的键值对的时候,我们就无法得到一样的 hashCode值了。因为我们后来创建的“键”对象已经不是存入HashMap中的那个内存地址的对象了。 

      我们看一个简单的例子,就能更加清楚的理解上面的意思。假定我们写了一个类:Person (人),我们判断一个对象“人”是否指向同一个人,只要知道这个人的身份证号一直就可以了。 

      先来个没有重写Code类的hashcode()的例子吧,看看是什么效果:

Java代码  技术分享

  1. package com.fit;  

  2.   

  3. import java.util.HashMap;  

  4.   

  5. /** 

  6.  * 身份证类 

  7.  *  

  8.  * @author ZYD 

  9.  *  

  10.  */  

  11. public class Code {  

  12.   

  13.     /** 

  14.      * 身份证号码,一旦确定就不能更改 

  15.      */  

  16.     private final int id;  

  17.   

  18.     public int getId() {  

  19.         return id;  

  20.     }  

  21.   

  22.     /** 

  23.      * 通过构造方法确定身份证号码 

  24.      *  

  25.      * @param id 

  26.      */  

  27.     public Code(int id) {  

  28.         this.id = id;  

  29.     }  

  30.   

  31.     /** 

  32.      * 重写equals()方法 

  33.      */  

  34.     public boolean equals(Object o) {  

  35.         // 如果地址一样,则两个对象相同  

  36.         if (this == o) {  

  37.             return true;  

  38.         }  

  39.         // 如果两个对象是同一类型,则比较其属性值是否都相同。如果都相同,则说明两个对象也相同;否则,说明这两个对象不相同。  

  40.         if (o instanceof Code) {  

  41.             Code co = (Code) o;  

  42.             boolean b = (co.id == this.id);  

  43.             return b;  

  44.         }  

  45.         return false;  

  46.     }  

  47.   

  48.     /** 

  49.      * 重写toString()方法 

  50.      */  

  51.     public String toString() {  

  52.         return "【身份证】:" + id;  

  53.     }  

  54.       

  55.     /** 

  56.      * 测试 

  57.      * @param args 

  58.      */  

  59.     public static void main(String[] args) {  

  60.           

  61.          HashMap<Code, Person> map = new HashMap<Code, Person>();  

  62.            

  63.          Person p1 = new Person(new Code(10001),"张三");  

  64.          Person p2 = new Person(new Code(10002),"李四");  

  65.            

  66.          map.put(p1.getCode(), p1);  

  67.          map.put(p2.getCode(), p2);  

  68.            

  69.          System.out.println("HashMap 中存放的人员信息:\n"+map);  

  70.            

  71.          //张三改名为张山,身份证号不变。  

  72.          Person p3 = new Person(new Code(10001),"张山");  

  73.          map.put(p3.getCode(), p3);  

  74.            

  75.          System.out.println("张三改名为张山后 HashMap 中存放的人员信息:\n"+map);  

  76.            

  77.          //查找身份证为10001 的人员信息  

  78.          System.out.println("查找身份证为:10001 的人员信息:"+map.get(new Code(10001)));  

  79.     }  

  80. }  

  81.   

  82. /** 

  83.  * 人类 

  84.  * @author Administrator 

  85.  * 

  86.  */  

  87. class Person {  

  88.   

  89.     /** 

  90.      * 每一个成人都有一个身份证 

  91.      */  

  92.     private Code code;  

  93.   

  94.     /** 

  95.      * 姓名 

  96.      */  

  97.     private String name;  

  98.   

  99.     public Code getCode() {  

  100.         return code;  

  101.     }  

  102.   

  103.     public void setCode(Code code) {  

  104.         this.code = code;  

  105.     }  

  106.   

  107.     public String getName() {  

  108.         return name;  

  109.     }  

  110.   

  111.     public void setName(String name) {  

  112.         this.name = name;  

  113.     }  

  114.   

  115.     public Person() {  

  116.   

  117.     }  

  118.   

  119.     public Person(Code code, String name) {  

  120.         this.code = code;  

  121.         this.name = name;  

  122.     }  

  123.   

  124.     /** 

  125.      * 重写equals()方法 当两个人得身份证号相同以及姓名相同时,表示这两个人是同一个人。 

  126.      */  

  127.     public boolean equals(Object o) {  

  128.         if (o == this) {  

  129.             return true;  

  130.         }  

  131.         if (o instanceof Person) {  

  132.             Person p = (Person) o;  

  133.             boolean b = this.code.equals(p.code) && this.name.equals(p.name);  

  134.             return b;  

  135.         }  

  136.         return false;  

  137.     }  

  138.   

  139.     /** 

  140.      * 重写toString()方法 

  141.      */  

  142.     public String toString() {  

  143.         return "【姓名】:" + name + "  ";  

  144.     }  

  145. }  

 

 

运行结果:

 

HashMap 中存放的人员信息:
{【身份证】:10002=【姓名】:李四  , 【身份证】:10001=【姓名】:张三  }
张三改名为张山后 HashMap 中存放的人员信息:
{【身份证】:10002=【姓名】:李四  , 【身份证】:10001=【姓名】:张三  , 【身份证】:10001=【姓名】:张山  }
查找身份证为:10001 的人员信息:null

 

从上面的结果可以看出:


我们所做的更新和查找操作都失败了。失败的原因就是我们的身份证类: Code 没有覆写 hashCode() 方法。这个时候,当查找一样的身份证号码的键值对的时候,使用的是默认的对象的内存地址来进行定位。这样,后面的所有的身份证号对象

new Code(10001) 产生的 hashCode () 值都是不一样的,所以导致操作失败。

 

 


 重写Code类的hashcode(),代码上:

 

Java代码  技术分享

  1. package com.fit;  

  2.   

  3. import java.util.HashMap;  

  4.   

  5. /** 

  6.  * 身份证类 

  7.  *  

  8.  * @author ZYD 

  9.  *  

  10.  */  

  11. public class Code {  

  12.   

  13.     /** 

  14.      * 身份证号码,一旦确定就不能更改 

  15.      */  

  16.     private final int id;  

  17.   

  18.     public int getId() {  

  19.         return id;  

  20.     }  

  21.   

  22.     /** 

  23.      * 通过构造方法确定身份证号码 

  24.      *  

  25.      * @param id 

  26.      */  

  27.     public Code(int id) {  

  28.         this.id = id;  

  29.     }  

  30.   

  31.     /** 

  32.      * 重写equals()方法 

  33.      */  

  34.     public boolean equals(Object o) {  

  35.         // 如果地址一样,则两个对象相同  

  36.         if (this == o) {  

  37.             return true;  

  38.         }  

  39.         // 如果两个对象是同一类型,则比较其属性值是否都相同。如果都相同,则说明两个对象也相同;否则,说明这两个对象不相同。  

  40.         if (o instanceof Code) {  

  41.             Code co = (Code) o;  

  42.             boolean b = (co.id == this.id);  

  43.             return b;  

  44.         }  

  45.         return false;  

  46.     }  

  47.   

  48.     /** 

  49.      * 重写hashcode()方法,以身份证号码作为hash码。 

  50.      *  

  51.      * @return 

  52.      */  

  53.     public int hashCode() {  

  54.         return id;  

  55.     }  

  56.   

  57.     /** 

  58.      * 重写toString()方法 

  59.      */  

  60.     public String toString() {  

  61.         return "【身份证】:" + id;  

  62.     }  

  63.       

  64.     /** 

  65.      * 测试 

  66.      * @param args 

  67.      */  

  68.     public static void main(String[] args) {  

  69.           

  70.          HashMap<Code, Person> map = new HashMap<Code, Person>();  

  71.            

  72.          Person p1 = new Person(new Code(10001),"张三");  

  73.          Person p2 = new Person(new Code(10002),"李四");  

  74.            

  75.          map.put(p1.getCode(), p1);  

  76.          map.put(p2.getCode(), p2);  

  77.            

  78.          System.out.println("HashMap 中存放的人员信息:\n"+map);  

  79.            

  80.          //张三改名为张山,身份证号不变。  

  81.          Person p3 = new Person(new Code(10001),"张山");  

  82.          map.put(p3.getCode(), p3);  

  83.            

  84.          System.out.println("张三改名为张山后 HashMap 中存放的人员信息:\n"+map);  

  85.            

  86.          //查找身份证为10001 的人员信息  

  87.          System.out.println("查找身份证为:10001 的人员信息:"+map.get(new Code(10001)));  

  88.     }  

  89. }  

  90.   

  91. /** 

  92.  * 人类 

  93.  * @author Administrator 

  94.  * 

  95.  */  

  96. class Person {  

  97.   

  98.     /** 

  99.      * 每一个成人都有一个身份证 

  100.      */  

  101.     private Code code;  

  102.   

  103.     /** 

  104.      * 姓名 

  105.      */  

  106.     private String name;  

  107.   

  108.     public Code getCode() {  

  109.         return code;  

  110.     }  

  111.   

  112.     public void setCode(Code code) {  

  113.         this.code = code;  

  114.     }  

  115.   

  116.     public String getName() {  

  117.         return name;  

  118.     }  

  119.   

  120.     public void setName(String name) {  

  121.         this.name = name;  

  122.     }  

  123.   

  124.     public Person() {  

  125.   

  126.     }  

  127.   

  128.     public Person(Code code, String name) {  

  129.         this.code = code;  

  130.         this.name = name;  

  131.     }  

  132.   

  133.     /** 

  134.      * 重写equals()方法 当两个人得身份证号相同以及姓名相同时,表示这两个人是同一个人。 

  135.      */  

  136.     public boolean equals(Object o) {  

  137.         if (o == this) {  

  138.             return true;  

  139.         }  

  140.         if (o instanceof Person) {  

  141.             Person p = (Person) o;  

  142.             boolean b = this.code.equals(p.code) && this.name.equals(p.name);  

  143.             return b;  

  144.         }  

  145.         return false;  

  146.     }  

  147.   

  148.     /** 

  149.      * 重写toString()方法 

  150.      */  

  151.     public String toString() {  

  152.         return "【姓名】:" + name + "  ";  

  153.     }  

  154. }  

 


 

 

运行效果:

 

HashMap 中存放的人员信息:
{【身份证】:10001=【姓名】:张三  , 【身份证】:10002=【姓名】:李四  }
张三改名为张山后 HashMap 中存放的人员信息:
{【身份证】:10001=【姓名】:张山  , 【身份证】:10002=【姓名】:李四  }
查找身份证为:10001 的人员信息:【姓名】:张山 


以上是关于为啥重写equals方法,一定要重写HashCode方法?的主要内容,如果未能解决你的问题,请参考以下文章

JAVA中重写equals()方法为什么要重写hashcode()方法说明

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

为啥要重写toString方法和hashcode方法

list.contains方法既然是调用equ 方法 还用重写 hashcod吗

JAVA 为啥要重写equals 方法才能对一个值进行操作(容器)

java中重写Object类的equals方法为啥要重写hashcode方法?不重写可以吗?