当我们需要将自己的类存入HashMapHashSet时一般都要重写其equalshashCode方法,但在重写时要符合规范否则会出问题。

1、equals方法

首先equals方法需要满足如下几点性质:

  • 自反性:对于非空引用xx.equals(x)的的结果一定为真。
  • 对称性:对于非空引用xy,如果x.equals(y)为真,y.equals(x)一定为真。
  • 传递性:对应非空引用xyz,如果x.equals(y)为真,y.equals(z)为真,x.euqals(z)一定为真。
  • 幂等性:对于非空引用xy,如果两个对象没有被改变,多次调用x.equals(y),其返回值不变。

这些性质中自反性与幂等性一般不会被破化,但对称性与传递性在一些情况下却无法满足。

1.1、对称性

  1. public final class CaseInsensitiveString {
  2. private final String s;
  3.  
  4. @Override // 不满足对称性
  5. public boolean equals(Object o) {
  6. if (o instanceof CaseInsensitiveString)
  7. return s.equalsIgnoreCase(((CaseInsensitiveString) o).s);
  8. if (o instanceof String)
  9. return s.equalsIgnoreCase((String) o);
  10. return false;
  11. }
  12. }

上边的例子,CaseInsensitiveString在实现equals方法时对原始String类进行了支持。

  1. CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
  2. String s = "polish";

考虑变量cisscis.equals(s)结果为真,但是s.equals(cis)为假,不满足对称性。

  1. List<CaseInsensitiveString> list = new ArrayList<>();
  2. list.add(cis);

这时list.contains(s)的结果为假。 

1.2、传递性  

  1. public class Point {
  2. private final int x;
  3. private final int y;
  4.  
  5. @Override public boolean equals(Object o) {
  6. if (!(o instanceof Point))
  7. return false;
  8. Point p = (Point)o;
  9. return p.x == x && p.y == y;
  10. }
  11. }

以一个二维坐点类为例,如果我们需要对其进行扩展,添加一个颜色属性。

  1. public class ColorPoint extends Point {
  2. private final Color color;
  3. public ColorPoint(int x, int y, Color color) {
  4. super(x, y);
  5. this.color = color;
  6. }
  7. }

ColorPoint该如何实现equals方法呢,先看第一种实现方式:

  1. // 不满足对称性
  2. @Override public boolean equals(Object o) {
  3. if (!(o instanceof ColorPoint))
  4. return false;
  5. return super.equals(o) && ((ColorPoint) o).color == color;
  6. }

这时PointColorPoint比较(point.equals(colorPoint))结果可以为真,但是ColorPointPoint比较时结果永远为假,即不满足对称性。第二种实现方式:

  1. // 不满足传递性
  2. @Override public boolean equals(Object o) {
  3. if (!(o instanceof Point))
  4. return false;
  5. if (!(o instanceof ColorPoint))
  6. return o.equals(this);
  7. return super.equals(o) && ((ColorPoint) o).color == color;
  8. }  

这种方式在比较时区别对待Point跟ColorPoint,但假如有三个对象(1, 2 ,红),(1,2),(1,2,绿),(1,2,红)等于(1,2),(1,2)等于(1,2,绿),但是(1,2,红)却不等于(1,2,绿),即不满足传递性。

1.3、模板

实现equals方法一般可以遵循一个模板,首先用==操作符检查被比较对象是否是自己本身,然后用instanceof操作符检查类型,再将其转换为正确的类型,最后逐个对有意义的字段进行比较。

  1. public final class PhoneNumber {
  2. private final int areaCode, prefix, lineNum;
  3.  
  4. @Override public boolean equals(Object o) {
  5. if (o == this)
  6. return true;
  7. if (!(o instanceof PhoneNumber))
  8. return false;
  9. PhoneNumber pn = (PhoneNumber)o;
  10. return pn.lineNum == lineNum && pn.prefix == prefix
  11. && pn.areaCode == areaCode;
  12. }
  13. }

2、hashCode方法

重写equals方法后一定要记得重写hashCode方法,因为hashCode也要满足几条性质: 

  • 当一个对象的属性没有被修改时,多次调用其hashCode函数返回值不变。
  • 如果两个对象被equals函数判定为相等,那么这两个对象的hashCode函数的返回值也一定相等。
  • 如果两个对象被equals函数判定为不等,那么这两个对象的hashCode函数的返回值可以是相等的。

这里说的对象的属性是指在equals函数中使用到的属性。上述性质中提到,如果两个对象被equals函数判定为相等,那这两个对象的hashCode函数的返回值必须是相等的,如果我们的类没有重写hashCode函数就无法满足这条性质,在使用HashMap函数时也会出问题。

  1. Map<PhoneNumber, String> m = new HashMap<>();
  2. m.put(new PhoneNumber(707, 867, 5309), "Jenny");  

接着调用m.get(new PhoneNumber(707, 867,5309)),但它并不会返回"Jenny"而是null,因为PhoneNumber类没有重写hashCode函数,根据Object类的hashCode函数,新对象可能会被映射到另外的哈希桶中导致查找失败。

重写hashCode函数也有章可循:

  1. 声明一个int类型的变量result,其值初始化为第一个属性的哈希值。
  2. 对于每个剩余的属性,分别计算其哈希值c
  3. 合并结果result = 31 * result + c

分别计算对象属性哈希值时,根据属性的类别,方法如下:

  1. 原始类型:使用Type.hashCode(f)Type是原始类型的装箱类型。
  2. 引用类型:调用该对象的hashCode函数,如果对象为null则使用默认值,一般为0。
  3. 数组类型:使用Arrays.hashCode函数,如果数组为null则使用默认值,一般为0。

对于上文中的PhoneNumber类,它的hashCode函数实现如下:

  1. @Override public int hashCode() {
  2. int result = Short.hashCode(areaCode);
  3. result = 31 * result + Short.hashCode(prefix);
  4. result = 31 * result + Short.hashCode(lineNum);
  5. return result;
  6. }

  

equals与hashCode的更多相关文章

  1. How to implement equals() and hashCode() methods in Java[reproduced]

    Part I:equals() (javadoc) must define an equivalence relation (it must be reflexive, symmetric, and ...

  2. JAVA中用堆和栈的概念来理解equals() "=="和hashcode()

    在学习java基本数据类型和复杂数据类型的时候,特别是equals()"=="和hashcode()部分时,不是很懂,也停留了很长时间,最后终于有点眉目了. 要理解equals() ...

  3. 关于equals、hashcode和集合类的小结

    一.首先明确一点:equals()方法和hashcode()方法是Object类里的方法. 查看源码可以知道,在Object类中equals(obj)方法直接返回的是  this == obj 的值. ...

  4. Object方法equals、hashCode

    java知识背景: 1)hashCode()方法返回的是Jvm的32位地址 2)==比较的是对象在jvm中的地址 3)Object的equals()比较的就是jvm物理地址 4)比较2个对象使用equ ...

  5. Java中的equals和hashCode方法

    本文转载自:Java中的equals和hashCode方法详解 Java中的equals方法和hashCode方法是Object中的,所以每个对象都是有这两个方法的,有时候我们需要实现特定需求,可能要 ...

  6. Java提高篇——equals()与hashCode()方法详解

    java.lang.Object类中有两个非常重要的方法: 1 2 public boolean equals(Object obj) public int hashCode() Object类是类继 ...

  7. Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例(转)

    Java中==.equals.hashcode的区别与重写equals以及hashcode方法实例  原文地址:http://www.cnblogs.com/luankun0214/p/4421770 ...

  8. java中equals和hashCode方法的解析

    解析Java对象的equals()和hashCode()的使用 前言 在Java语言中,equals()和hashCode()两个函数的使用是紧密配合的,你要是自己设计其中一个,就要设计另外一个.在多 ...

  9. Java实战equals()与hashCode()

    一.equals()方法详解 equals()方法在object类中定义如下: 代码 public boolean equals(Object obj) { return (this == obj); ...

  10. 一次性搞清楚equals和hashCode

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

随机推荐

  1. Redis面试大全

    1. 什么是Redis Redis是由意大利人Salvatore Sanfilippo(网名:antirez)开发的一款内存高速缓存数据库.Redis全称为:Remote Dictionary Ser ...

  2. maven-archetype-plugin 的正确打开方式

    1.  准备好一个编辑好的模板工程 2. 在 pom.xml 中添加 maven-archetype-plugin 插件 <plugin> <groupId>org.apach ...

  3. SQL经典实例笔记

    目录 前言 第一章:检索记录 在Where字句中使用别名 前言 本文是根据我阅读的书籍SQL经典实例而写的笔记,只记载我觉得有价值的内容 第一章:检索记录 在Where字句中使用别名 --错误实例 s ...

  4. mongodb 分组求最大值

    先上代码 db.getCollection("playback").aggregate([ {$match:{"game_record_id":{$in:[68 ...

  5. [EXP]CVE-2019-9621 Zimbra<8.8.11 GetShell Exploit(配合Cscan可批量)

    发现时间 2019年03月18日 威胁目标 采用Zimbra邮件系统的企业 主要风险 远程代码执行 攻击入口 localconfig.xml  配置文件 使用漏洞 CVE-2019-9621 受影响应 ...

  6. 使用 Java 移除字符串中的前导零

    给定一串数字,从中删除前导零. public class Test { public static void main(String[] args) { String str = "0000 ...

  7. day23——继承

    day23 初识继承 字面意思:儿子可以完全使用父亲的所有内容 专业角度:如果B类继承A类, B类就称为子类.派生类 A类就称为父类.基类.超类 面向对象三大特性:继承.封装.多态 继承:单继承.多继 ...

  8. leetcode的Hot100系列--347. 前 K 个高频元素--hash表+直接选择排序

    这个看着应该是使用堆排序,但我图了一个简单,所以就简单hash表加选择排序来做了. 使用结构体: typedef struct node { struct node *pNext; int value ...

  9. 解决html 图片缓存问题

    <!--问题:上传一张图片,通过js更新src属性刷新图片使其即时显示时, 当img的src当前的url与上次地址无变化时(只更改图片,名称不变,不同图片名称相同)图片不变化(仍显示原来的图片) ...

  10. java之spring之对象的创建

    首先提供目录结构如下: 下面提供各文件代码,以供参考: UserDynamicFactory.java package cn.sxt.factory; import cn.sxt.vo.User; / ...