HashMap中对象作Key为什么要重写equals和hashcode

哈哈!我摊牌了!阿里和美团的面试官都问过我同样的问题,你确定不了解一下?

HashMap中对象作Key为什么要重写equals和hashcode

今天咱们唠点简单的,这是在 初级开发及校招 面试中经常问的一个问题。

  • HashMapkey为一个对象的时候要注意什么 ?

  • 为什么要同时重写equals和hashcode方法 ?

给你十秒钟想想你该怎么答… :watch:

想不出来没关系,看了这篇文章后面试遇到同样的问题就是 送分题 。:closed_book:

HashMap中对象作Key为什么要重写equals和hashcode

什么是equals和hashcode方法

我们知道Java中所有的类都继承于Object类及Object类是所有类的父类。当子类调用一个方法时,如果该方法没有被重写则需要往上面找到父类中的方法执行。

Object类

public boolean equals(Object obj) {
        return (this == obj);
 }
 public native int hashCode();
复制代码

在Object类中,hashCode是一个本地方法简单理解为 获取对象地址 ,equals方法比较自己和obj 对象地址 是否相等。在这里一定先认识到这两个方法,一个是取地址,一个是比较地址。

下面进入正题…

对象做Key会怎样

下面以一段小代码演示一下,其输出的结果是 null 。先说一句:在这里我们认为我的a和b是有着相同属性的同一个对象(key),我可以通过hashMap.get(b)获取到字符串 hello。但事与愿违。

public class NoHashCodeAndEquals {
    public static void main(String[] args) {
        Object o = new Object();
        HashMap<Demo, String> hashMap = new HashMap<>();
        Demo a = new Demo("A");
        Demo b = new Demo("A");
        hashMap.put(a, "hello");
        String s = hashMap.get(b);
        System.out.println(s);

    }
}
class Demo {
    String key;

    Demo(String key) {
        this.key = key;
    }
}
复制代码

可能大家一眼看过去感觉没问题,a和b不是同一个对象呀!地址肯定不一样!打住!你在说什么,我刚刚说什么来着 ? :point_up_2: 蒙圈了吧!:dizzy_face:(ps:我自己也快说懵圈了 )。

HashMap中对象作Key为什么要重写equals和hashcode

稳住!!!

咱们简单看两行HashMap源码,看完立马就清醒。

HashMap中对象作Key为什么要重写equals和hashcode
  • 获取hashcode计算桶下标,存放元素,看上去没什么毛病就是计算下标而已。对!只是调用key的hashCode计算一个下标值。
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
复制代码

看到了hashCode方法,咱们就还差一个equals方法,HashMap中对于equals()方法的调用在哪些位置呢?

  • put方法(以JDK1.7为例)
public V put(K key, V value) {
...
int hash = hash(key);
// 确定桶下标
int i = indexFor(hash, table.length);
// 先找出是否已经存在键为 key 的键值对,如果存在的话就更新这个键值对的值为 value
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
...
// 插入新键值对
addEntry(hash, key, value, i)
return null;
}
复制代码
  • get方法(以JDK1.7为例)
public V get(Object key) {  
    if (key == null)  
        return getForNullKey();  
    int hash = hash(key.hashCode());  
    for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) {  
        Object k;  
        if (e.hash == hash && ((k = e.key) == key || key.equals(k)))  
            return e.value;  
    }  
    return null;  
}
复制代码

如上所示:当HashMap调用插入或获取方法,需要将值(key)对应的哈希码与数组中的哈希码比较,相等时,则会通过equals方法比较key值是否相等。

再结合上面我写的代码案例,缓一缓咱们再品一下!

Demo a = new Demo("A");
Demo b = new Demo("A");
复制代码

这两行的代码的含义,我们理解为定义了两个 相同含义 (认为是同一个key)的key对象,但是大家都知道这两个key的hashcode方法的值是不一样的。

在HashMap中的比较key是这样的,先求出key的hashcode(),比较其值是否相等,若相等再比较equals(),若相等则认为他们是相等的。若equals()不相等则认为他们不相等。

  • 如果只重写hashcode()不重写equals()方法,当比较equals()时,其实调用的是Object中的方法,只是看他们是否为同一对象(即进行内存地址的比较)。
  • 如果只重写equals()不重写hashcode()方法,在一个判断的时候就会被拦下HashMap认为是不同的Key。

所以想以对象作为HashMap的key, 必须重写该对象的hashCode和equals方法 。确保hashCode相等的时候equals的值也是true。

  • 图解如下:
HashMap中对象作Key为什么要重写equals和hashcode

对于这个问题看似简单,可很多初级程序员竟然说不出一个所以然,一方面是对HashMap不熟悉另外就是对这两个方法的含义理解不透彻。

最后再次总结一句:在HashMap的“键”部分存放自定义的对象, 一定 要重写 equalshashCode 方法。再来两句老生常谈的话!

  • 两个对象==相等,则其hashcode一定相等,反之不一定成立。
  • 两个对象equals相等,则其hashcode一定相等,反之不一定成立。

自己再品品呢 ~

原文 

https://juejin.im/post/5ef9f7ade51d45347f3bcfc1

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » HashMap中对象作Key为什么要重写equals和hashcode

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址