HashMap之equals和hashCode小陷阱

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

 

01public 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),重点看如下两行:

 

1if (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方法中打印一行文字。如下:

01public 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方法,如下,看看这次的结果会是怎样。

 

01public 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方法。

转载于:https://www.cnblogs.com/heartstage/p/3391933.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值