zoukankan      html  css  js  c++  java
  • Java Hash集合的equals()与hashCode() 方法

      Java 集合实现类,无论是HashSet、HashMap等所有的Hash算法实现的集合类(后面简称Hash集合),加入的对象必须实现 hashCode() 与 equals() 方法,稍微不同的地方是:HashSet 需要对整个对象实现两个方法,而HashMap 只需要对作为key的对象实现这两个方法。因为向Hash集合存入一个元素时,Hash集合会调用该对象的hashCode()方法来得到该对象的hashCode的值,然后根据hashCode的值决定该对象在Hash集合中存储位置。如果两个元素通过equals()方法比较返回true,但它们的hashCode()方法返回值不相等,Hash集合将会把它们视为不同的对象,两个元素都可以添加到Hash集合里。所以Hash集合判断两个元素是否相等的标准是:两个对象通过equals()方法比较相等,并且两个元素的hashCode()方法返回值也相等。

     

    重写hashCode()方法的规则

    ① 同一个对象多次调用hashCode()方法返回值相同

    ② 两个对象通过equals()方法比较返回true 时,这两个对象的hashCode()方法返回相等的值

    ③ 对象中用于equals() 方法比较标准的变量,都应该用于计算hashCode值

     

    重写hashCode方法一般步骤

    ① 把对象内每个有意义的变量计算出一个int 型的hashCode值

    ② 用第1步计算出来的多个hashCode值组合成一个hashCode值返回

    ③ 为了避免直接相加产生偶然相等,可以通过为各变量的hashCode值乘以任意质数再相加

     

    案例:

    ① 首先来看一个没有重写equals() 与 hashCode() 方法的对象,添加到HashSet 时会出现什么情况。

    我们创建一个Person类,包含两个成员变量,id 与 name,如果两个对象的这两个属性都不等我们才认为它们是不同的对象,我们没有重写equals() 与 hashCode() 方法,但是我们希望HashSet 集合对同一个对象只能保存一份,有相同对象加入时,加入失败。

     1 class Person
     2 {
     3     private String name;
     4     private int id; 
     5     
     6     public Person()
     7     {}
     8     
     9     public Person(int id,String name)
    10     {
    11         this.id = id;
    12         this.name = name;
    13     }
    14     
    15     public int getId() {
    16         return id;
    17     }
    18 
    19     public void setId(int id) {
    20         this.id = id;
    21     }
    22 
    23     public String getName() {
    24         return name;
    25     }
    26     public void setName(String name) {
    27         this.name = name;
    28     }
    31 }
     1         
     2         HashSet haSe = new HashSet();
     3         
     4         //相同id与name的添加两次
     5         haSe.add(new Person(1001, "latiny"));
     6         haSe.add(new Person(1001, "latiny"));
     7         
     8         //遍历HashSet
     9         for(Object obj:haSe)
    10         {
    11             Person p = (Person)obj;
    12             System.out.println(p.getId()+" "+p.getName()+" "+p.hashCode());
    13         }
    14         

      输出结果:

    1001 latiny 355165777
    1001 latiny 1807500377

    可以看到name与id相同的两个对象都成功的加入到了HashSet集合,因为它们的hashCode值不一样,这显然没有达到预期的效果,如果要实现预期的效果需要重写Person类 equals() 与 hashCode() 方法。

     

    ② 重写Person类equals() 与 hashCode() 方法

    在上面的Person类添加如下代码:

     1     //重写hashCode() 方法
     2     public int hashCode() {
     3         int hash = 7;
     4         hash = hash*31 + this.id*11 + this.name.hashCode()*31;
     5         return hash;
     6     }
     7 
     8     //重写equals() 方法
     9     public boolean equals(Object obj) {
    10         if(this==obj)
    11         {
    12             return true;
    13         }
    14         //如果两个对象的id与name都相等,则两个对象相等
    15         if(obj.getClass()==Person.class)
    16         {
    17             Person p=(Person)obj;
    18             return this.id==p.id && this.name==p.name;
    19         }
    20         else
    21         {
    22             return false;
    23         }
    24     }

      

      重新执行代码:

     1         
     2         HashSet haSe = new HashSet();
     3         
     4         //相同id与name的对象添加两次
     5         haSe.add(new Person(1001, "latiny"));
     6         System.out.println(haSe.add(new Person(1001, "latiny")));
     7         
     8         //遍历HashSet
     9         for(Object obj:haSe)
    10         {
    11             Person p = (Person)obj;
    12             System.out.println(p.getId()+" "+p.getName()+" "+p.hashCode());
    13         }
    14         

      输出结果: 

    false
    1001 latiny -46445433

    可以看到 id与 name 相同的两个对象只有一个加入到了HashSet集合,第二次添加时失败因为我们重写了equals() 与 hashCode() 方法,只要Person类对象id与name都相同,equals()返回true,hashCode() 返回值相同,HashSet集合就会将这两个对象视为同一个对象。

     

    HashMap 的使用与HashSet类似,作为key的对象也需要重写 equals() 与 hashCode() 方法,不然也会出现将相同对象作为key值成功插入到HashMap中。

    ① 未重写 equals() 与 hashCode() 方法

     1 class Person
     2 {
     3     private String name;
     4     private int id; 
     5     
     6     public Person()
     7     {}
     8     
     9     public Person(int id,String name)
    10     {
    11         this.id = id;
    12         this.name = name;
    13     }
    14     
    15     public int getId() {
    16         return id;
    17     }
    18 
    19     public void setId(int id) {
    20         this.id = id;
    21     }
    22 
    23     public String getName() {
    24         return name;
    25     }
    26     public void setName(String name) {
    27         this.name = name;
    28     }
    29 
    30 }

      

      将Person类作为HashMap的key

     1         
     2         HashMap<Person, String> hash = new HashMap<Person, String>();
     3         hash.put(new Person(1, "latiny"), "周瑜");
     4         hash.put(new Person(1, "latiny"), "曹操");
     5         hash.put(new Person(1, "latiny"), "刘禅");
     6         
     7         //foreach遍历HashMap
     8         for(Object key:hash.keySet())
     9         {
    10             String name = hash.get(key);
    11             System.out.println(key+"-->"+name+" "+key.hashCode());
    12         }
    13         
    14         Object obj = hash.get(new Person(1, "latiny"));
    15         String name = (String) obj;
    16         System.out.println(name);

      输出结果:

    com.latiny.set.Person@544a5ab2-->刘禅 1414159026
    com.latiny.set.Person@152b6651-->曹操 355165777
    com.latiny.set.Person@6bbc4459-->周瑜 1807500377
    null

    可以看到,id 与 name 相同的Person对象作为 key 重复添加到HashMap集合中。

    更为严重的问题是,当我们想要以相同的 id 与 name的Person对象作为key去获取value 时,结果竟然是null,为什么呢?因为这个Person对象的id 与 name 与之前的三个对象相同,但是在内存中它却是一个新的对象,有自己独立的空间,即有自己独立的hashCode值,由于我们没有重写hashCode方法,它的hashCode计算方法还是按照Object这个类来实现的。

     

    ② 重写 equals() 与 hashCode() 方法之后

     1 class Person
     2 {
     3     private String name;
     4     private int id; 
     5     
     6     public Person()
     7     {}
     8     
     9     public Person(int id,String name)
    10     {
    11         this.id = id;
    12         this.name = name;
    13     }
    14     
    15     public int getId() {
    16         return id;
    17     }
    18 
    19     public void setId(int id) {
    20         this.id = id;
    21     }
    22 
    23     public String getName() {
    24         return name;
    25     }
    26     public void setName(String name) {
    27         this.name = name;
    28     }
    29 
    30     
    31     //重写hashCode() 方法
    32     public int hashCode() {
    33         int hash = 7;
    34         hash = hash*31 + this.id*11 + this.name.hashCode()*31;
    35         return hash;
    36     }
    37 
    38     //重写equals() 方法
    39     public boolean equals(Object obj) {
    40         if(this==obj)
    41         {
    42             return true;
    43         }
    44         //如果两个对象的id与name都相等,则两个对象相等
    45         if(obj.getClass()==Person.class)
    46         {
    47             Person p=(Person)obj;
    48             return this.id==p.id && this.name==p.name;
    49         }
    50         else
    51         {
    52             return false;
    53         }
    54     }
    55     
    56 }

      执行测试代码

     1         
     2         HashMap<Person, String> hash = new HashMap<Person, String>();
     3         hash.put(new Person(1, "latiny"), "周瑜");
     4         hash.put(new Person(1, "latiny"), "曹操");
     5         hash.put(new Person(1, "latiny"), "刘禅");
     6         
     7         //foreach遍历HashMap
     8         for(Object key:hash.keySet())
     9         {
    10             String name = hash.get(key);
    11             System.out.println(key+"-->"+name+" "+key.hashCode());
    12         }
    13         
    14         Object obj = hash.get(new Person(1, "latiny"));
    15         String name = (String) obj;
    16         System.out.println(name);
    17         

      输出结果:

    com.latiny.set.Person@fd3b218f-->刘禅 -46456433
    刘禅

    可以看到,最后一次添加的元素将之前添加的都覆盖了,因为我们重写的方法判断如果Person类的id与name相同,equals()返回true, hashCode() 返回相同的值,HashMap认定它们key值相同,会将之前加入的元素覆盖。我们也可以将具有相同的id与name的Person类作为key值去访问value。

     

     

  • 相关阅读:
    论分治与归并思想
    关于缩短cin时间的方法
    【lower_bound、upperbound讲解、二分查找、最长上升子序列(LIS)模版】
    getDomain(url)-我的JavaScript函数库-mazey.js
    jQuery-PHP跨域请求数据
    ASP-Server.Transfer-Response.Redirect
    jQuery获取相邻标签的值
    分界线<hr/>
    jQuery获取input复选框的值
    Bootstrap支持的JavaScript插件
  • 原文地址:https://www.cnblogs.com/Latiny/p/8359088.html
Copyright © 2011-2022 走看看