之前一直不是很理解为什么要重写HashCode和Equals方法,才只能作为键值存储在HashMap中。通过下文,可以一探究竟。

首先,如果我们直接用以下的Person类作为键,存入HashMap中,会发生发生什么情况呢?

public class Person {

private String id;

public Person(String id) {

this.id = id;

}

}

import java.util.HashMap;

public class Main {

public static void main(String[] args) {

HashMap<Person, String> map = new HashMap<Person, String>();

map.put(new Person("001"), "findingsea");

map.put(new Person("002"), "linyin");

map.put(new Person("003"), "henrylin");

map.put(new Person("003"), "findingsealy");

System.out.println(map.toString());

System.out.println(map.get(new Person("001")));

System.out.println(map.get(new Person("002")));

System.out.println(map.get(new Person("003")));

}

}

那么输出结果是什么呢?

{Person@6e4d4d5e=henrylin, Person@275cea3=findingsea, Person@15128ee5=findingsealy, Person@4513098=linyin}

null

null

null

我们可以看到,这里出现了两个问题:

  1. 在添加的过程中,我们将key=new Person("003")的键值对添加了两次,那么在期望中,HashMap中应该只存在一对这样的键值对,因为key(期望中)是相同的,所以不应该重复添加,第二次添加的value="findingsealy"应该替换掉原先的value="henrylin"。但是在输入中,我们发现期望中的情况并没有出现,而是在HashMap同时存在了value="findingsealy"和value="henrylin"的两个键值对,并且它们的key值还是不相同的,这显然是错误的。
  2. 在获取value值时,我们分别用三个Person对象去查找,这三个对象和我们刚刚存入的三个key值(在期望中)是相同的,但是查找出的却是三个null值,这显然也是错误的。

那么,正确的方法其实在很多地方都是被描述过了,直接对Person类进行修改,重载equals和hashCode方法,修改过后的Person类如下:

public class Person {

private String id;

public Person(String id) {

this.id = id;

}

@Override

public boolean equals(Object o) {

if (this == o) return true;

if (o == null || getClass() != o.getClass()) return false;

Person person = (Person) o;

if (id != null ? !id.equals(person.id) : person.id != null) return false;

return true;

}

@Override

public int hashCode() {

return id != null ? id.hashCode() : 0;

}

}

那么,当我们重新执行上述的检验程序时,得到的结果如下:

{Person@ba31=findingsea, Person@ba32=linyin, Person@ba33=findingsealy}

findingsea

linyin

findingsealy

可以看到,之前指出的亮点错误都得到了改正。那么,为什么会这样呢?

在HashMap中,查找key的比较顺序为:

  1. 计算对象的Hash Code,看在表中是否存在。
  2. 检查对应Hash Code位置中的对象和当前对象是否相等。

显然,第一步就是要用到hashCode()方法,而第二步就是要用到equals()方法。在没有进行重载时,在这两步会默认调用Object类的这两个方法,而在Object中,Hash Code的计算方法是根据对象的地址进行计算的,那两个Person("003")的对象地址是不同的,所以它们的Hash Code也不同,自然HashMap也不会把它们当成是同一个key了。同时,在Object默认的equals()中,也是根据对象的地址进行比较,自然一个Person("003")和另一个Person("003")是不相等的。

理解了这一点,就很容易搞清楚为什么需要同时重载hashCode()和equals两个方法了。

  • 重载hashCode()是为了对同一个key,能得到相同的Hash Code,这样HashMap就可以定位到我们指定的key上。
  • 重载equals()是为了向HashMap表明当前对象和key上所保存的对象是相等的,这样我们才真正地获得了这个key所对应的这个键值对。

还有一个细节,在Person类中对于hashCode()的重在方法为:

@Override

public int hashCode() {

return id != null ? id.hashCode() : 0;

}

这里可能有疑惑的点在于:为什么可以用String类型的变量的Hash Code作为Person类的Hash Code值呢?这样new Person(new String("003"))和new Person(new String("003"))的Hash Code是相等的吗?

来看看以下代码的输出:

System.out.println("findingsea".hashCode());

System.out.println("findingsea".hashCode());

System.out.println(new String("findingsea").hashCode());

System.out.println(new String("findingsea").hashCode());

728795174

728795174

728795174

728795174

可以看到四条语句的输出都是相等的,很直观的合理的猜测就是String类型也重载了hashCode()以根据字符串的内容来返回Hash Code值,所以相同内容的字符串具有相同的Hash Code。

同时,这也说明了一个问题:为什么在已知hashCode()相等的情况下,还需要用equals()进行比较呢?就是因为避免出现上述例子中的出现的情况,因为根据对Person类的hashCode()方法的重载实现,Person类会直接用id这个String类型成员的Hash Code值作为自己的Hash Code值,但是很显然的,一个Person("003")和一个String("003")是不相等的,所以在hashCode()相等的情况下,还需要用equals()进行比较。

以下例子可以作为上述说明的佐证:

System.out.println(new Person("003").hashCode()); // 47667

System.out.println(new String("003").hashCode()); // 47667

System.out.println(new Person("003").equals(new String("003"))); // false

以上即是全部。

参考:https://segmentfault.com/a/1190000002655085

HashMap中使用自定义类作为Key时,为何要重写HashCode和Equals方法的更多相关文章

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

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

  2. java集合框架(hashSet自定义元素是否相同,重写hashCode和equals方法)

    /*HashSet 基本操作 * --set:元素是无序的,存入和取出顺序不一致,元素不可以重复 * (通过哈希值来判断是否是同一个对象) * ----HashSet:底层数据结构是哈希表, * 保证 ...

  3. 对象作为 map 的 key 时,需要重写 equals 方法和 hashCode 方法

    对象作为 map 的 key 时,需要重写 hashCode 和 equals方法 如果没有重写 hashCode 方法,那么下面的代码示例会输出 null 我们首先定义一个对象:BmapPoint, ...

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

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

  5. 关于HashMap自定义key重写hashCode和equals的问题

     使用HashMap,如果key是自定义的类,就必须重写hashcode()和equals() hashcode()和equals()都继承于object,在Object类中的定义为: equals( ...

  6. 关于java中的hashcode和equals方法原理

    关于java中的hashcode和equals方法原理 1.介绍 java编程思想和很多资料都会对自定义javabean要求必须重写hashcode和equals方法,但并没有清晰给出为何重写此两个方 ...

  7. Java 中正确使用 hashCode 和 equals 方法

    在这篇文章中,我将告诉大家我对hashCode和equals方法的理解.我将讨论他们的默认实现,以及如何正确的重写他们.我也将使用Apache Commons提供的工具包做一个实现. 目录: hash ...

  8. (转)Java 中正确使用 hashCode 和 equals 方法

    背景:最近在编写持久化对象时候遇到重写equals和hashCode方法的情况,对这两个方法的重写做一个总结. 链接:https://www.oschina.net/question/82993_75 ...

  9. Java中正确使用hashCode和equals方法

    在这篇文章中,我将告诉大家我对hashCode和equals方法的理解.我将讨论他们的默认实现,以及如何正确的重写他们.我也将使用Apache Commons提供的工具包做一个实现. 目录: hash ...

随机推荐

  1. 7-41 jmu-python-最佳身高 (10 分)

    最佳的情侣身高差遵循着一个公式:(女方的身高)×1.09 =(男方的身高).下面就请你写个程序,为任意一位用户计算他/她的情侣的最佳身高. 输入格式: 输入第一行给出正整数N(≤10),为前来查询的用 ...

  2. fsLayuiPlugin联动表格使用(一)

    简单联动表格使用 点击主表格,加载副表格数据, 演示地址:http://fslayuiplugin.fallsea.com/views/linkageDatagrid/index.html 联动表格配 ...

  3. C++泛化双端队列

    循环双端队列 双端队列可以在队首和队尾进行入队操作.出队操作的特殊队列. 循环双端队列是充分利用空间,使用格外的数据存储队头和队尾,这里利用数组进行实现. 循环双端队列(CircleQueue.h) ...

  4. 每日一点:git 与 github 区别

    絮絮叨叨在前:以前的公司,都用svn 进行代码管理.最近我那程序猿先生真的受不了我,强迫我使用tortoiseGit. 一开始对于 git 和 github 傻傻分不清,干脆自己整理资料,总结一下. ...

  5. elasticsearch-head 安装

    一.安装phantomjs(由于入坑多写一步,此步骤可省掉) 1.下载phantomjs 安装npm的时候会依赖phantomjs 所以我们先安装phantomjs phantomjs 下载地址:ht ...

  6. ES6的函数

    1,带参数默认值的函数 JS函数有个独特的行为:可以接受任意数量的参数,而无视函数声明的形参数量.未提供的参数会使用默认值来代替.实际传递的参数允许少于或多于正式指定的参数. 在ES6中可以直接在形参 ...

  7. 随着php7的发布我个人觉得有必要进行一下历史回顾和整理

    先看下人尽皆知的发展历史: HP 继承自一个老的工程,名叫 PHP/FI.PHP/FI 在 1995 年由 Rasmus Lerdorf 创建,最初只是一套简单的 Perl 脚本,用来跟踪访问他主页的 ...

  8. idea最下方视图中没有spring框解决方法

    之前遇到过idea打开一个项目后,如图所示的spring视图框消失不见了. 并且view-Tool windows里面也找不到的问题;因为没有这个的话还挺烦的,网上搜了好久都没有资料,所以记录一下; ...

  9. C语言程序设计(三) 简单的算术运算和表达式

    第三章 简单的算术运算和表达式 算数运算符:一元.二元.三元(条件运算符) 1/2是整型除法,1.0/2是浮点数除法 求余运算(%)限定参与运算的两个操作数必须为整数,不能对两个实型数据进行求余运算 ...

  10. 我用STM32MP1做了个疫情监控平台3—疫情监控平台实现

    目录 1.前言 2.数据接口的获取 3.Qt界面的实现 4.在开发板上运行Qt程序 5.使用无线模块联网 6.代码下载 @ 1.前言 之前我使用桌面版本Qt实现了肺炎疫情监控平台:基于Qt的新冠肺炎疫 ...