zoukankan      html  css  js  c++  java
  • HashMap之equals和hashCode小陷阱

    先以一段代码开始这篇blog。

    01 public class Name {
    02  
    03   private String first; //first name
    04   private String last;  //last name
    05  
    06   public String getFirst() {
    07     return first;
    08   }
    09  
    10   public void setFirst(String first) {
    11     this.first = first;
    12   }
    13  
    14   public String getLast() {
    15     return last;
    16   }
    17  
    18   public void setLast(String last) {
    19     this.last = last;
    20   }
    21  
    22   public Name(String first, String last) {
    23     this.first = first;
    24     this.last = last;
    25   }
    26  
    27   @Override
    28   public boolean equals(Object object) {
    29     Name name = (Name) object;
    30  
    31     return first.equals(name.getFirst()) && last.equals(name.getLast());
    32   }
    33  
    34   public static void main(String[] args) {
    35     Map<Name, String> map = new HashMap<Name, String>();
    36     map.put(new Name("mali", "sb"), "yes");
    37      
    38     System.out.println("is the key existed? ture or false? -> "
    39         + map.containsKey(new Name("mali", "sb")));
    40   }
    41  
    42 }

    那输出结果是什么呢?类似这样的题目总能遇到,以前不知道有什么好考的,弱智?自己动手尝试了一次,发现结果不是自己想象的那样。本篇就用来揭示HashMap的equals与hashCode中你不知道的秘密。结果如下:

    is the key existed? ture or false? -> false 

    对的,结果就是false,我很不理解为什么这样,已经重写了equals函数了啊!当时真心不服气,就在equals函数里面打了断点,然后更让我难以置信的事情发生了,断点处没有停。非常困惑,不过还好,jdk的源码在手上,去查了HashMap中containsKey函数的源码。源码结构如下图:

    从图中可以看出,真正干活的是getEntry(Object key),重点看如下两行:

    1 if (e.hash == hash &&
    2                 ((k = e.key) == key || (key != null && key.equals(k))))
    3     return e;

    从if条件上看,是一个短路与,首先要判断两个对象的hash值是否相等。如果相等才进行后续的判断。或者换一个说法,在HashMap中只有两个对象的hash值相等的前提下才会执行equals方法的逻辑。关于这一点,有两个佐证。

    在stackoverflow上找到一篇关于HashMap不执行equals方法的文章,回答中有明确给出这样的答案。Java HashMap.containsKey() doesn’t call equals()

    自己编程验证。

    在文章开头的基础上,做了点儿改进,输出两个对象的hash值,并且在equals方法中打印一行文字。如下:

    01 public class Name {
    02  
    03   private String first; //first name
    04   private String last;  //last name
    05  
    06   public String getFirst() {
    07     return first;
    08   }
    09  
    10   public void setFirst(String first) {
    11     this.first = first;
    12   }
    13  
    14   public String getLast() {
    15     return last;
    16   }
    17  
    18   public void setLast(String last) {
    19     this.last = last;
    20   }
    21  
    22   public Name(String first, String last) {
    23     this.first = first;
    24     this.last = last;
    25   }
    26  
    27   @Override
    28   public boolean equals(Object object) {
    29     System.out.println("equals is running...");
    30     Name name = (Name) object;
    31  
    32     return first.equals(name.getFirst()) && last.equals(name.getLast());
    33   }
    34  
    35   public static void main(String[] args) {
    36     Map<Name, String> map = new HashMap<Name, String>();
    37     Name n1 = new Name("mali", "sb");
    38     System.out.println("the hashCode of n1 : " + n1.hashCode());
    39     map.put(n1, "yes");
    40     Name n2 = new Name("mali", "sb");
    41     System.out.println("the hashCode of n2 : " + n2.hashCode());
    42     System.out.println("is the key existed? ture or false? -> "
    43         + map.containsKey(n2));
    44   }
    45  
    46 }

    结果:

    the hashCode of n1 : 1690552137  
      the hashCode of n2 : 1901116749 
       is the key existed? ture or false? -> false 

    从执行结果可以看出1、两个对象的hash值是不相同的;2、equals方法确实也没有执行。

    再次对代码进行改进,加入重写的hashCode方法,如下,看看这次的结果会是怎样。

    01 public class Name {
    02  
    03   private String first; //first name
    04   private String last;  //last name
    05  
    06   public String getFirst() {
    07     return first;
    08   }
    09  
    10   public void setFirst(String first) {
    11     this.first = first;
    12   }
    13  
    14   public String getLast() {
    15     return last;
    16   }
    17  
    18   public void setLast(String last) {
    19     this.last = last;
    20   }
    21  
    22   public Name(String first, String last) {
    23     this.first = first;
    24     this.last = last;
    25   }
    26  
    27   @Override
    28   public boolean equals(Object object) {
    29     System.out.println("equals is running...");
    30     Name name = (Name) object;
    31  
    32     return first.equals(name.getFirst()) && last.equals(name.getLast());
    33   }
    34  
    35   public int hashCode() {
    36     System.out.println("hashCode is running...");
    37     return first.hashCode() + last.hashCode();
    38   }
    39  
    40   public static void main(String[] args) {
    41     Map<Name, String> map = new HashMap<Name, String>();
    42     Name n1 = new Name("mali", "sb");
    43     System.out.println("the hashCode of n1 : " + n1.hashCode());
    44     map.put(n1, "yes");
    45     Name n2 = new Name("mali", "sb");
    46     System.out.println("the hashCode of n2 : " + n2.hashCode());
    47     System.out.println("is the key existed? ture or false? -> "
    48         + map.containsKey(n2));
    49   }
    50  
    51 }

    结果:

    hashCode is running... 
       the hashCode of n1 : 3347552  
      hashCode is running... 
       hashCode is running...  
      the hashCode of n2 : 3347552  
      hashCode is running...  
      equals is running...  
      is the key existed? ture or false? -> true 

    同样从结果中可以看出:在hash值相等的情况下,equals方法也执行了,HashMap的containsKey方法也像预想的那样起作用了。

    结论:

    在使用HashSet(contains也是调用HashMap中的方法)、HashMap等集合时,如果用到contains系列方法时,记得需同时重写equals与hashCode方法。

  • 相关阅读:
    cmd常用命令
    SqlServer 、MySQL查询库中表明 字段信息
    每科成绩大于80分 查询 删除重复记录
    1
    go语言体系学习(一):环境准备与变量
    PriorityQueue及二叉堆
    LinkedList的几个元素操作方法
    判定字符是否唯一的面试题想到
    python爬虫利器 scrapy和scrapy-redis 详解一 入门demo及内容解析
    mongodb 数据操作CRUD
  • 原文地址:https://www.cnblogs.com/heartstage/p/3391933.html
Copyright © 2011-2022 走看看