使用HashMap,如果key是自定义的类,就必须重写hashcode()和equals()

hashcode()和equals()都继承于object,在Object类中的定义为:

equals()方法在Object类中的定义:

public boolean equals(Object obj){
    return (this == obj);
}

equals()的定义为:

public native int hashCode();

是一个本地方法,返回的对象的地址值。

 1.hashcode()和equals()是在哪里被用到的?什么用的?

HashMap是基于散列函数,以数组和链表的方式实现的。

而对于每一个对象,通过其hashCode()方法可为其生成一个整形值(散列码),而默认自定义类Student 的hashcode是根据对象的引用算的,该整型值被处理后,将会作为数组下标,存放该对象所对应的Entry(存放该对象及其对应值)。

上边由于两个new Student(1,"aa")是不一样的Student对象。而默认的Student类的hashcode是根据对象的引用算的。所以直接认为是两个不一样的对象,直接put进去了。所以需要重写hashcode方法,如果hashcode不一样则直接认为是不同对象

equals()方法则是在HashMap中插入值或查询时会使用到。当HashMap中插入 值或查询值对应的散列码与数组中的散列码相等时,则会通过equals方法比较key值是否相等,所以想以自建对象作为HashMap的key,必须重写 该对象继承object的equals方法。

2.本来不就有hashcode()和equals()了么?干嘛要重写,直接用原来的不行么?

HashMap中,如果要比较key是否相等,要同时使用这两个函数!因为自定义的类的hashcode()方法继承于Object类,其hashcode码为默认的内存地 址,这样即便有相同含义的两个对象,比较也是不相等的,例如,

Student st1 = new Student("wei","man");

Student st2 = new Student("wei","man");

正常理解这两个对象再存入到hashMap中应该是相等的,但如果你不重写 hashcode()方法的话,比较是其地址,不相等!

HashMap中的比较key是这样的,先求出key的hashcode(),比较其值是否相等,若相等再比较equals(),若相等则认为他们是相等 的。若equals()不相等则认为他们不相等。如果只重写hashcode()不重写equals()方法,当比较equals()时只是看他们是否为 同一对象(即进行内存地址的比较),所以必定要两个方法一起重写。HashMap用来判断key是否相等的方法,其实是调用了HashSet判断加入元素 是否相等。

 
HashSet内部是通过HashMap实现。只有使用排序的时候才使用TreeMap。否知使用HashMap。
 
结果set内的元素为3个,没有去处重复的new Student(1,"aa") ?为什么呢?
这里由于两个new Student(1,"aa")是不一样的Student对象。而默认的Student类的hashcode是根据对象的引用算的。所以直接认为是两个不一样的对象,直接put进去了。所以需要重写hashcode方法,如果hashcode不一样则直接认为是不同对象,如下:
 
发现还是不对,还是put进去了呢?
这里重写的hashcode是一样的,所以还是put进去了。所以还需要重新equals方法。其实是有这样一个规定,如果hahscode一样时,则还需要继续调用equals方式看看对象是否相等。如下即可实现:

输出:

可以看到如果hashcode不一样就直接认为是不一样的对象,不需要再去equal比较,更加节省时间。
如果new Student(1,"aa")、new Student(1,"bb")。通过code和name算出的hashcode就可以算是不一样的对象,就不需要再去equals比较。
往往HashSet中存放的对象是否相等的逻辑都需要自己定义,而并不会直接用默认的引用来算,即一般都会重新hashcode和equals方法,而且同时需要重写。以后要注意哦。

HashMap的put和get也类似。
HashMap是底层实现时数组加链表。
        A.当put元素时:
              1.首先根据put元素的key获取hashcode,然后根据hashcode算出数组的下标位置,如果下标位置没有元素,直接放入元素即可。
              2.如果该下标位置有元素(即根据put元素的key算出的hashcode一样即重复了),则需要已有元素和put元素的key对象比较equals方法,如果equals不一样,则说明可以放入进map中。这里由于hashcode一样,所以得出的数组下标位置相同。所以会在该数组位置创建一个链表,后put进入的元素到放链表头,原来的元素向后移动。       
        B.当get元素时:
             根据元素的key获取hashcode,然后根据hashcode获取数组下标位置,如果只有一个元素则直接取出。如果该位置一个链表,则需要调用equals方法遍      历链表中的所有元素与当前的元素比较,得到真正想要的对象。
  可以看出如果根据hashcdoe算出的数组位置尽量的均匀分布,则可以避免遍历链表的情况,以提高性能。
   所以要求重写hashcode时,也要重写equals方法。以保证他们是相同的比较逻辑

 

关于HashMap自定义key重写hashCode和equals的问题的更多相关文章

  1. 为什么要重写hashcode和equals方法?初级程序员在面试中很少能说清楚。

    我在面试 Java初级开发的时候,经常会问:你有没有重写过hashcode方法?不少候选人直接说没写过.我就想,或许真的没写过,于是就再通过一个问题确认:你在用HashMap的时候,键(Key)部分, ...

  2. (转)为什么要重写 hashcode 和 equals 方法?

    作者丨hsm_computer cnblogs.com/JavaArchitect/p/10474448.html 我在面试Java初级开发的时候,经常会问:你有没有重写过hashcode方法?不少候 ...

  3. 为什么要重写hashcode和equals方法

    我在面试 Java初级开发的时候,经常会问:你有没有重写过hashcode方法?不少候选人直接说没写过.我就想,或许真的没写过,于是就再通过一个问题确认:你在用HashMap的时候,键(Key)部分, ...

  4. HashMap中使用自定义类作为Key时,为何要重写HashCode和Equals方法

    之前一直不是很理解为什么要重写HashCode和Equals方法,才只能作为键值存储在HashMap中.通过下文,可以一探究竟. 首先,如果我们直接用以下的Person类作为键,存入HashMap中, ...

  5. 重写hashcode和equals方法

    重写hashcode和equals方法 简乐君 2019-05-07 21:55:43 35481 收藏 191分类专栏: Java 文章标签: equals() hashcode()版权 一.前言我 ...

  6. 面试题:我们重写一个对象的时候为什么要同时重写hashcode()和equals()方法

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 在创建的类不重写hashCode()和equals() 方法时,默认使用 java 提供的 java.l ...

  7. Java 重写hashCode()与equals()

    为什么要重写hashCode() 和 equals() equals() 默认的Object类里面equals()方法是根据对象所在的内存来做判断的,如果两个对象引用指向的是同一个内存,则返回true ...

  8. 【java编程】重写HashCode和equals方法

    [一]重写equals方案的规则 equals方法本来的原则 1.类的每个实例本质上都是唯一的. 2.不关心类是否提供了“逻辑相等”的测试功能 3.超类已经覆盖了equals,从超类继承过来的行为对于 ...

  9. 使用HashMap,如果key是自定义的类,就必须重写hashcode()和equals()

    java编程里有关约定:如果两个对象根据equals方法比较是相等的,那么调用这两个对象的任意一个hashcode方法都必须产生相同的结果. hashcode()和equals()都继承于object ...

随机推荐

  1. Maven项目打包成可执行Jar文件

    在使用Maven完成项目以后,如果需要打包成可执行的Jar文件,我们通过eclipse的导出很麻烦,还得指定入口文件的位置,还得说明依赖的jar包,既然都使用Maven了,很重要的一个目的就是让这些繁 ...

  2. Kafka文件存储机制那些事

    Kafka是什么 Kafka是最初由Linkedin公司开发,是一个分布式.分区的.多副本的.多订阅者,基于zookeeper协调的分布式日志系统(也可以当做MQ系统),常见可以用于web/nginx ...

  3. PHP序列化变量的4种方法

    序列化是将变量转换为可保存或传输的字符串的过程:反序列化就是在适当的时候把这个字符串再转化成原来的变量使用.这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性. 1.  serialize ...

  4. 互联网推送服务原理:长连接+心跳机制(MQTT协议)

    互联网推送消息的方式很常见,特别是移动互联网上,手机每天都能收到好多推送消息,经过研究发现,这些推送服务的原理都是维护一个长连接(要不不可能达到实时效果),但普通的socket连接对服务器的消耗太大了 ...

  5. sqlserver 级联删除、级联更新

    增加外键约束时,设置级联更新.级联删除:[ ON DELETE { NO ACTION | CASCADE | SET NULL | SET DEFAULT } ][ ON UPDATE { NO A ...

  6. Jquery Ajax 调用后台并返回数据

    一.前台调用ajax并解析json对象. $.ajax({ url : '', type : 'POST', //GET data : '’, beforeSend : function(reques ...

  7. 【协议】2、TCP/IP协议三次握手与四次握手流程解析

    一.TCP报文格式  TCP/IP协议的详细信息参看<TCP/IP协议详解>三卷本.下面是TCP报文格式图:图1 TCP报文格式  上图中有几个字段需要重点介绍下:  (1)序号:Seq序 ...

  8. Shaping Regions(dfs)

    Shaping Regions Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 124  Solved: 39[Submit][Status][Web B ...

  9. NULL 和 0

    Question: What is the difference from NULL and "0"? Example: return NULL; return 0; Answer ...

  10. blfs(systemv版本)学习笔记-前几章节的脚本配置

    我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! 记录blfs书籍前几个章节的配置内容. bash shell启动文件章节 1.切换root用户 su 2.创建/etc/prof ...