本文章主要讨论和回答一下几个问题:

  • equals()的四大特性

  • equals()和hashcode()之间的关系,为什么我们经常说这两个方法要么都重写,要么都不重写?

  • HashMap、HashSet等容器为什么要求一定要重写equals()以及hashcode()

equals()

equals和hashcode方法我们都很了解,是Object类中的定义的方法,这意味着所有的类都隐式实现了这两个方法。

Object类中的equals方法的默认实现是比较对象标识(根据对象头信息),但是这个对我们没有任何意义。因此一般情况下我们要重写equals方法

equals方法一般有以下四个约定:

  • 自反:对象必须等于自身
  • 对称:x.equals(y) 必须返回与 y.equals(x) 相同的结果
  • 传递性:如果 x.equals(y) 和 y.equals(z) 那么 x.equals(z)
  • 一致:仅当包含在 equals() 中的属性发生更改时,equals() 的值才应更改

使用IDEA智能重写equals方法如下,比较两个对象相关属性的值是否全部一致。:

public class Student {
private String name;
private int age;
}
    @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}

hashcode()

hashcode方法也同样定义在Object类中,返回一个整数,表示该类的实例状态,要根据类的相等性定义来计算这个值,也就是说hashcode方法调用了equals方法,因此重写hashcode必须先重写equals,这样类的hashcode值才有意义。

hashcode同样有取得共识的约定:

  • 内部一致性:只有当 equals() 中的属性发生变化时,hashCode() 的值才会发生变化
  • 相等一致性:彼此相等的对象必须返回相同的 hashCode
  • hash碰撞:不相等的对象可能具有相同的哈希码

从第二个约定我们可以推出,重写equals方法也必须同时重写hashcode方法,不然就违反了第二个规定

看到这里,我想我们已经解决了前面提出的第二个问题,equals和hashcode必须都被重写或者都不重写

但是,这只是一个约定,并非强制要求,如果不遵循这个约定会有什么问题呢?我们通过hashmap来举例

hashmap的key如何实现唯一性

我们知道map为了保证map的key是唯一的,我们需要重写key类的hashcode方法和equals方法。为什么呢?因为key的添加过程是这样的:

  • 先查看key的hashcode是否已经存在

    • 如果不存在,说明当前容器没有此key,直接添加
    • 如果存在,有可能是相同的key,也有可能是产生了hash碰撞。使用equals进行进一步比较

因此使用hashmap必须重写这两个方法

如果不重写的话,可能会有重复的key被放入map中。举个例子:

        HashMap<Student, Integer> studentIntegerHashMap = new HashMap<Student, Integer>();
Student tom1 = new Student("tom", 11);
Student tom2 = new Student("tom", 11);
studentIntegerHashMap.put(tom1,1);
studentIntegerHashMap.put(tom2,1);

正常情况下tom2是不会被添加到map集合中的,但是如果你不重写hashcode方法,使用的就是本地的hashcode方法,这两个对象的hashcode一定不同,因此都能被添加进集合中,这显然是我们不想看到的。

至于HashSet,有的朋友应该知道,HashSet的底层是通过HashMap实现的,因此也同样要实现这两个方法才能“去重”

总结

在本篇文章中,我们讨论了 equals() 和 hashCode() 的约定和使用。我们应该记住以下几点:

  • 如果我们覆盖 equals(),则始终覆盖 hashCode(),反过来也一样
  • 考虑使用 IDE 或第三方库来生成 equals() 和 hashCode() 方法

参考

关于equals()和hashcode()的一些约定的更多相关文章

  1. equals()与hashCode()方法协作约定

    翻译人员: 铁锚 翻译时间: 2013年11月15日 原文链接: Java equals() and hashCode() Contract 图1 Java所有对象的超类 java.lang.Obje ...

  2. 一次性搞清楚equals和hashCode

    前言 在程序设计中,有很多的“公约”,遵守约定去实现你的代码,会让你避开很多坑,这些公约是前人总结出来的设计规范. Object类是Java中的万类之祖,其中,equals和hashCode是2个非常 ...

  3. equals()和hashCode()区别?

    equals()和hashCode()区别? ------------------------------------------------- equals():反映的是对象或变量具体的值,即两个对 ...

  4. 探索equals()和hashCode()方法

    探索equals()和hashCode()方法 在根类Object中,实现了equals()和hashCode()这两个方法,默认: equals()是对两个对象的地址值进行的比较(即比较引用是否相同 ...

  5. Effective Java 第三版——10. 重写equals方法时遵守通用约定

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  6. java的equals()与hashCode()以及包装类中的实现

    1. hashcode 1.1 hashcode来源 1.2 hashcode的形式 1.3 hashcode目的 1.4 hashcode规则 1.5 hashcode作用体现 1.6 重写hash ...

  7. Java == ,equals 和 hashcode 的区别和联系(阿里面试)

    今天阿里的人问我 equals 与hashcode的区别,我答不上来, 仔细查了一下,做了总结: (1) == 这是Java 比较内存地址,就是内存中的对象: java中的==是比较两个对象在JVM中 ...

  8. 为什么要同时重写equals和hashcode

    原文地址https://blog.csdn.net/tiantiandjava/article/details/46988461 原文地址https://blog.csdn.net/lijiecao0 ...

  9. 第8条:覆盖equals时请遵守通用约定

    第8条:覆盖equals时请遵守通用约定 引言:尽管Object是一个具体类,但是设计它主要是为了拓展.它所有的非final方法(equals.hashCode.toString.clone和fina ...

随机推荐

  1. DOM0和DOM2事件的应用和区别详细对比

    1.触发次数 零级事件只能注册一次,如果注册多次,后面的会覆盖前面的 btn.onclick = function () { alert(1) } btn.onclick = function () ...

  2. java基础——参数的应用

    命令行传递参数(少用) 有时候,你希望运行一个程序时,在传递给它信息.这个时候,要靠 传递命令行 参数 给main()函数实现. package com.dong.method;​public cla ...

  3. ruby基础(二)

    ruby语法基础 1.方法 方法时对象定义的与该对象相关的操作.在Ruby中,对象的所有的操作都被封装成 方法. 语法糖:语法糖是一种为了照顾一般人的习惯而产生的特殊语法. ruby中一切数据都是对象 ...

  4. Linux学习之路-Linux-at及cron命令【7】---20171215

    Linux学习之路-Linux-at及cron命令[7]---20171215 DannyExia000人评论986人阅读2017-12-24 17:28:03   ntpdate 命令 [root@ ...

  5. Docker Swarm(五)Config 配置管理

    前言 在动态的.大规模的分布式集群上,管理和分发配置文件也是很重要的工作.传统的配置文件分发方式(如配置文件放入镜像中,设置环境变量,volume 动态挂载等)都降低了镜像的通用性. Docker 1 ...

  6. 045.Python线程队列

    线程队列 1 基本语法和用法 put 往线程队列里防止,超过队列长度,直接阻塞 get 从队列中取值,如果获取不到,直接阻塞 put_nowait: 如果放入的值超过队列长度,直接报错(linux) ...

  7. 2.1PyCharm 的初始设置

    PyCharm 的初始设置(知道) 目标 恢复 PyCharm 的初始设置 第一次启动 PyCharm 新建一个 Python 项目 设置 PyCharm 的字体显示 PyCharm 的升级以及其他 ...

  8. 原生基础js脚本实现--在线答题系统

    全部代码在最下面----需要的直接往下翻 html方面的代码  : 正确的答案 value=s <!DOCTYPE html> <html lang="en"&g ...

  9. 在ssm框架测试中解决javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException

    在单元测试发现causeBy:javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException 经发现是db.p ...

  10. Proteus中包含的传感器类型(Transducers)

    1. 传感器列表 2. 部分传感器的测量电路 (1)光照传感器,搭采样电阻,测电压输出. (2)距离传感器,带采样电阻,测电压输出. (3)粉尘传感器,测PWM脉宽 其余传感器多为总线类型的传感器,各 ...