equals()方法

  如果满足了以下任何一个条件,就不需要覆盖equals()方法:

  1 类的每个实例本质上都是唯一的。

  2 不关心类是否提供了“逻辑相等”的测试功能。

  3 父类已经覆盖了equals,从父类继承过来的行为对于子类也是合适的。

  4 类是私有的或是包内私有的,可以确定它的equals方法永远不会被调用。

  如果类具有自己特有的“逻辑相等”概念(不同于对象等同的概念),而且父类没有覆盖equals()方法以实现期望的行为,就需要覆盖equals()方法。这通常属于“值类”的情形。值类仅仅是一个表示值的类,例如Integer或者Date。在用equals()方法比较值对象的引用时,希望知道它们在逻辑上是否相等,而不是想了解它们是否指向同一个对象。

  单例类不需要覆盖equals()方法。因为对于这样的类,逻辑相同与对象等同是一回事,Object的equals()方法等同于逻辑意义上的equals()方法。

  覆盖equals()方法的通用约定如下:

  1 自反性

  对于任何非null的引用x,x.equals(x)必须返回true。

  2 对称性

  对于任何非null的引用x和y,仅当x.equals(y)返回true时,y.equals(x)必须返回true。

  3 传递性

  对于任何非null的引用x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回true。

  4 一致性

  对于任何非null的引用x和y,如果比较的两个对象没有被修改,那么多次调用x.equals(y)就会一致地返回true,或者一致地返回false。

  5 非空性

  对于任何非null的引用x,x.equals(null)必须返回false。

  实现equals()方法的步骤如下:

  1 ==

  使用==操作符检查“参数是否为这个对象的引用”,即通过比较地址来判断两个对象是否等同。

  2 instanceof

  使用instanceof操作符检查“参数是否为正确的类型”,即通过instanceof操作符来判断比较的是否为同一个类的对象。

  3 强制类型转换

  把参数转换成正确的类型,即把参数Object对象强制转换成当前类的对象。

  4 匹配“关键”域

  对于该类中的每个“关键”域,检查参数中的域是否与该对象中对应的域相匹配,即判断两个对象包含的内容是否相同。

  对于既不是float也不是double类型的基本类型域,可以使用==操作符进行比较;对于对象引用域,可以递归地调用equals()方法;对于float域,可以使用Float.compare()方法;对于double域,则使用Double.compare()方法。对float和double域进行特殊的处理,是因为存在Float.NaN、-0.0f以及类似的double常量。对于数组域,则要把以上这些规则应用到每个元素上。如果数组域中的每个元素都很重要,就可以使用发行版本1.5中新增的其中一个Arrays.equals()方法。

  注意点如下:

  1 覆盖equals时总要覆盖hashCode。

  2 加上@Override注解,不要将参数Object对象替换为其他类型,因为替换之后方法不是重写,而是重载。

  String中的equals()方法如下:

 1 // 比较内容
 2 public boolean equals(Object anObject) {
 3     // 如果对象等同
 4     if (this == anObject) {
 5         return true;
 6     }
 7
 8     // 如果比较的是String对象
 9     if (anObject instanceof String) {
10         // 强制类型转换
11         String anotherString = (String)anObject;
12         // 获取当前String对象中的字符数组长度
13         int n = value.length;
14         // 如果两个字符串长度相等
15         // 否则,返回false
16         if (n == anotherString.value.length) {
17             // 定义字符数组指针
18             char v1[] = value;
19             char v2[] = anotherString.value;
20
21             // 依次比较等长部分的字符
22             int i = 0;
23             while (n-- != 0) {
24                 // 如果字符不同,则返回false
25                 if (v1[i] != v2[i])
26                     return false;
27                 i++;
28             }
29             return true;
30         }
31     }
32     return false;
33 }

  hashCode()方法

  Object的hashCode()是一个本地方法。

public native int hashCode();

  在每个覆盖了equals()方法的类中,必须覆盖hashCode()方法,即覆盖equals时总要覆盖hashCode。

  覆盖hashCode()方法的通用约定如下:

  1 同一个对象调用hashCode()方法返回同一个整数。

  2 如果equals()方法返回true,那么hashCode()方法返回的整数相等。

  3 如果equals()方法返回false,那么hashCode()方法返回的整数不一定不相等。但是应该知道,对于不相等的对象,产生不同的整数结果,有助于提高散列表的性能。

  如果不满足第1条,HashMap无法查找到插入的键值对。覆盖equals时没有覆盖hashCode而违反的关键约定是第2条:相等对象具有相等的散列码(hash code)。其中,相等对象指相同内容的不同对象。如果不覆盖hashCode()方法,相等对象调用hashCode()方法返回的整数不一定相等。当散列码不相等时,相等对象可能会被插入到不能包含相等对象作为key的集合(例如HashMap)中。与第2条互为逆否命题的是,如果hashCode()方法返回的整数不相等,那么equals()方法返回false。比较散列码有利于减少equals()方法的调用次数,提高效率。另外,如果hashCode()方法返回的整数相等,那么equals()方法不一定返回true。

  一个好的散列函数为不相等的对象产生不相等的散列码。理想情况下,散列函数应该把集合中不相等的实例均匀地分布到所有可能的散列值上。解决方法如下:

  1 把某个非0的常数值(例如17)保存在一个名为result的int类型变量中。

  2 对于equals()方法涉及的每个域,完成如下步骤:

  2.1 计算该域的散列码c。

  2.1.1 如果该域是boolean类型,则计算(f ? 1 : 0)。

  2.1.2 如果该域是byte、char、short或者int类型,则计算(int)f。

  2.1.3 如果该域是long类型,则计算(int)(f ^ (f >>> 32))。

  2.1.4 如果该域是float类型,则计算Float.floatToIntBits(f)。

  2.1.5 如果该域是double类型,则计算Double.doubleToLongBits(f),然后按照步骤2.1.3继续计算。

  2.1.6 如果该域是一个对象引用

  2.1.6.1 如果递归调用equals()方法来比较该域,则递归调用该域的hashCode()方法。

  2.1.6.2 否则,自行确定规则,计算该域的散列码。其中,如果该引用值为null,则返回0。

  2.1.7 如果该域是一个数组,则把每个重要的元素通过上述规则来计算,并根据2.2把这些散列码合并到result中。如果数组中的每个元素都重要,则可以调用JDK 1.5新增的Arrays.hashCode()方法。

  2.2 根据result = 31 * result + c;(31 * result会被JVM优化成(result << 5) - result),把之前得到的散列码c合并到result中。(31是奇素数,如果乘数是偶数,并且出现乘法溢出,则信息就会丢失,因为与2相乘等价于位运算向左移一位。使用素数的好处不很明显,但是习惯上使用素数来计算散列码。)

  3 返回result。

  在String中,只是字母顺序不同的所有字符串具有相等的散列码,代码如下:

 1 // 获取散列码
 2 public int hashCode() {
 3     // 获取字符串缓存散列码
 4     int h = hash;
 5     // 如果需要计算散列码
 6     if (h == 0 && value.length > 0) {
 7         // 定义字符数组指针
 8         char val[] = value;
 9
10         // 遍历每个字符
11         for (int i = 0; i < value.length; i++) {
12             // 31 * h会被JVM优化成(h << 5) - h
13             h = 31 * h + val[i];
14         }
15         // 修改字符串缓存散列码,避免重复计算
16         hash = h;
17     }
18     return h;
19 }

  参考资料

  《Effective Java 中文版 第2版》 第8条:覆盖equals时请遵守通用约定 P28-38

  《Effective Java 中文版 第2版》 第9条:覆盖equals时总要覆盖hashCode P39-43

  浅谈Java中的hashcode方法

  《Java编程思想》 P495

转载于:https://www.cnblogs.com/WJQ2017/p/8426444.html

Java equals()方法和hashCode()方法相关推荐

  1. java重写6,java重写equals()方法和hashCode()方法

    1.equals()方法和hashCode()方法是什么? equals()和hashCode()都是是Java中万物之源Object类中的方法: equals方法用于比较两个对象是否相同,Objec ...

  2. 详解 equals() 方法和 hashCode() 方法

    来源:编程迷思, www.cnblogs.com/kismetv/p/7191736.html 前言 Java的基类Object提供了一些方法,其中equals()方法用于判断两个对象是否相等,has ...

  3. HashSet要重写equals方法和hashCode方法

    注:重要笔记在代码中注释有 hashSet去重: 即判断两个对象是否相等 1:会先调用对象的hashCode方法获得hash的值,如果set中哈希表里面没有对应的hash值,则将次对象存入set中 2 ...

  4. equals方法和hashCode方法之间的那些事(1.1)

    我们先来看一下java官方 jdk中关于equals方法和hashCode方法的介绍: 不知道读者们注意到一个细节没有,就是: 注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 ...

  5. JAVA正确地自定义比较对象---如何重写equals方法和hashCode方法

    在实际应用中经常会比较两个对象是否相等,比如下面的Address类,它有两个属性:String province 和 String city. public class Address {privat ...

  6. java中重写hashcode_Java中HashSet要重写equals方法和hashCode方法

    下面给出一个属性不同但哈希码相同的例子: import java.util.HashSet; import java.util.Iterator; import java.util.Set; clas ...

  7. Java基础语法:重写equals方法和hasCode方法

    目录 一.为什么需要重写equals和hasCode方法 二.equals和hasCode解读 1.equals方法 2.hasCode方法 三.重写 四.参考资料 一.为什么需要重写equals和h ...

  8. 普歌-码灵团队-java中的equals方法和toString方法及总结

    普歌-码灵团队:Object类中的方法介绍及其用法 一.Object类 二.方法介绍 1.toString方法 2.重写equals方法: 3. 拓展 三.总结 一.Object类的简单介绍 二.Ob ...

  9. 为什么要重写toString()方法和hashcode()方法

    一.toString(): 在Object类里面定义toString()方法的时候返回的对象的哈希code码,这个hashcode码不能简单明了的表示出对象的属性.所以要重写toString()方法. ...

最新文章

  1. 用户 'IIS APPPOOL\**' 登录失败的解决方案(项目部署到本地IIS上打开网页出现报错)...
  2. 计算机导论的学科知识体,依托学科课程体系的《计算机导论》课程改革
  3. Linux 网络编程详解二(socket创建流程、多进程版)
  4. cglib动态代理和JDK动态代理
  5. php php5,初探 PHP5 (一)_PHP
  6. python def 参数一直为false_在Python 3中,如果参数为False,则查找惯用的方法来评估为False...
  7. 【转载保存】Java+Selenium使用
  8. 深度残差收缩网络:(一)背景知识
  9. 买房后每月还贷是什么感觉?
  10. easyexcel解析xls文件:Convert excel format exception.You can try specifying the ‘excelType‘ yourself
  11. 域名过期导致的问题-研究DNS相关知识
  12. SQL server 数据库分离成功后,但还是压缩不了,.mdf和.ldf文件拒绝访问
  13. 适合协作办公的在线Word文档-超级文档
  14. Ubuntu 16.04 amd64下deb安装gcc5.4和所有依赖
  15. Linux Kernel GFP_KERNEL
  16. MapReduce教程(01)- 初识MapReduce
  17. 孙子兵法——【12】(百家讲坛观后记录)
  18. oracle 删除主键级联删除唯一索引
  19. qt实现简单的视频播放器
  20. 协议分析实战:某航某凰知音用户名及密码提取

热门文章

  1. Maven--部署构件至 Nexus
  2. Spring mvc+ maven + MyBatis + Oracle + IDEA 项目搭建 - framework 进阶中(一)
  3. hibench测试出现问题--zookeeper
  4. 【shell编程基础0】bash shell编程的基本配置
  5. hdu-1862-EXCEL排序
  6. 泛海精灵软件预发布统计报告 反馈
  7. python boxplot pvalue_使用python和matplotlib获取boxplot中使用的值
  8. dmb显示服务器断开连接,dmb联网信息发布系统操作手册企业加强版.docx
  9. Shell脚本基础语法
  10. python 3.x 不再支持MySQLdb 模块