为什么重写 equals 的时候必须重写 hashCode

大家可能从很多教程中了解到:

SUN官方的文档中规定"如果重定义equals方法,就必须重定义hashCode方法,以便用户可以将对象插入到散列(哈希)表中"

那么 SUN 公司是出于什么考虑做了这个规定呢?

在集合框架中的HashSetHashTableHashMap都使用哈希表的形式存储数据,而hashCode计算出来的哈希码便是它们的身份证。哈希码的存在便可以:

  1. 快速定位对象,提高哈希表集合的性能。
  2. 只有当哈希表中对象的索引即hashCode和对象的属性即equals同时相等时,才能够判断两个对象相等。
  3. 从上面可以看出,哈希码主要是为哈希表服务的,其实如果不需要使用哈希表,也可以不重写hashCode。但是SUN公司应该是出于对程序扩展性的考虑(万一以后需要将对象放入哈希表集合中),才会规定重写equals的同时需要重写hashCode,以避免后续开发不必要的麻烦。

重写equals的注意事项

Java语言规范要求equals需要具有如下的特性:

  1. 自反性:对于任何非空引用 x,x.equals() 应该返回 true
  2. 对称性:对于任何引用 x 和 y,当且仅当 y.equals(x) 返回 truex.equals(y) 也应该返回 true
  3. 传递性:对于任何引用 x、y 和 z,如果 x.equals(y)返回 truey.equals(z) 也应返回同样的结果。
  4. 一致性:如果 x 和 y 引用的对象没有发生变化,反复调用 x.equals(y) 应该返回同样的结果。
  5. 对于任意非空引用 x,x.equals(null) 应该返回 false

在对象比较时,我们应该如何编写出一个符合特性的 equals 方法呢,《Core Java》中提出了如下建议:

  1. 显式参数命名为 otherObject,稍后将它转换成另一个叫做 other 的变量。
  2. 检测 this 与 otherObject 是否引用同一个对象:

    if (this == otherObject) return true;

    计算这个等式可以避免一个个比较类中的域,实现优化。

  3. 检测 otherObject 是否为 null,如果为 null,返回 false。进行非空校验是十分重要的。

  4. 比较 this 与 otherObject 是否属于同一个类。

    • 如果每个子类都重写了 equals,使用 getClass 检验:
    
    
    if (getClass() != otherObject.getClass())
    return false;
    
    
    • 如果所有子类都使用同一个 equals,就用 instanceof 检验:
    if (!(otherObject instanceof ClassName))
    return false;
  5. 将 otherObject 转换为相应的类型变量。

    ClassName other = (ClassName) otherObject;
  6. 现在可以对所有需要比较的域进行比较了。

    • 基本类型使用 == 比较
    • 对象使用 equals 比较
    • 数组类型的域可以使用静态方法 Arrays.equals检测相应数组元素是否相等
    • 如果所有域匹配,则返回 true

注意:子类重写父类 equals 方法时,必须完全覆盖父类方法,不能因为类型错误或者其他原因定义了一个完全无关的方法。可以使用 @Override 注解对覆盖父类的方法进行标记,这样编译器便会检测到覆盖过程中的错误。

重写 hashCode 的注意事项

散列码(hash code)是由对象导出的一个整型值。散列码没有规律,在不同的对象中通过不同的算法生成,Java中生成 hashCode 的策略为(以下说明均摘自 Java API 8):

  • String 类的 hashCode 根据其字符串内容,使用算法计算后返回哈希码。

    Returns a hash code for this string. The hash code for a String object is computed as s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

  • Integer 类返回的哈希码为其包含的整数数值。

    Returns: a hash code value for this object, equal to the primitive int value represented by this Integer object.

  • Object 类的 hashCode 返回对象的内存地址经过处理后的数值。

    Returns a hash code value for the object. This method is supported for the benefit of hash tables such as those provided by HashMap.

在自己的类中想要重写 hashCode 的话一般怎么做呢?建议合理地组合实例域的散列码,让各个不同对象产生的散列码更加均匀。例如我们现在有一个 Cat 对象,它有 namesize 和 color 三个不同域,那么可以重写 hashCode 方法如下:

class Cat {
......
public int hashCode() {
//hashCode是可以返回负值的
return 6 * name.hashCode()
+ 8 * new Double(size).hashCode()
+ 10 * color.hashCode();
}
......
}

当然还有更好的做法,我们可以直接调用静态方法 Objects.hash 并提供多个参数。这个方法会对各个参数调用 Object.hashCode,并组合返回的散列码。故以上的方法可以缩写为:

public int hashCode() {
return Objects.hash(name, size, color);
}

注意: equalshashCode的定义必须一致,两个对象equalstrue,就必须有相同的hashCode。例如:如果定义的equals比较的是小猫的 name,那么hashCode就需要散列该 name,而不是小猫的 color 或 size。


参考:

Java 重写 equals 与 hashCode 的注意事项的更多相关文章

  1. Java:重写equals()和hashCode()

    Java:重写equals()和hashCode() 1.何时需要重写equals() 当一个类有自己特有的“逻辑相等”概念(不同于对象身份的概念). 2.设计equals() [1]使用instan ...

  2. Java重写equals()和hashCode()

    1.何时需要重写equals() 当一个类有自己特有的 ”逻辑相等”概念(不同于对象身份的概念). 2.设计equals() [1]使用instanceof操作符检查 ”实参是否为正确的类型”. [2 ...

  3. java重写equals和hashCode方法

    一.重写equals方法 如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等. 利用equals比较八大包装对象(如int,f ...

  4. hashCode之二--Java:重写equals()和hashCode()

    以下内容总结自<Effective Java>. 1.何时需要重写equals() 当一个类有自己特有的“逻辑相等”概念(不同于对象身份的概念). 2.设计equals() [1]使用in ...

  5. Java 重写equals()与hashCode()方法

    List对象的contains方法实际上也是调用的equals()方法来进行逐条对比的. 示例代码: package com.imooc.collection; /** * 课程类 */ public ...

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

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

  7. java中equals与hashCode的重写问题

    这几天有一个朋友问我在重写equals和hashCode上出现了问题,最后我帮她解决了问题,同时也整理出来分享给大家 现上Object的equals与HashCode的代码 public boolea ...

  8. Java中equals()和hashCode()的关系以及重写equals()和hashCode()的重要性

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6580647.html  一:关系 如果两个对象相等(equal),它们的hashcode一定相同: 如果两个对 ...

  9. Java中equals,hashcode

         在Java语言中,Object对象中包含一个equals和hashCode方法,其中hashCode方法是由JVM本地代码(native code)实现的,返回值是一个有符号的32位整数,对 ...

随机推荐

  1. NFS网络文件系统的配置

    NFS网络文件系统的配置 NFS网络文件系统 NFS(network file system)网络文件系统.一种使用于分散式文件协定,有SUN公司开发.功能是通过网络让不同的机器.不同的操作系统能够分 ...

  2. wps文档忘记保存关闭了怎么恢复

    wps文档忘记保存关闭了怎么恢复 点击程序左上角的''WPS文字/表格/演示''选择备份管理,根据需要尝试右侧下面的"查看其他备份"功能就能找了. 点击"开始-运行&qu ...

  3. 文件——文件指针——文件练习(随机产生N个手机号)——自动关文件

    python 2 3file() #python 2读模式 写模式 追加模式 只要沾上了r,文件不存的就会报错读模式 r 读写模式 r+(可以写)1.不能写2.文件不存在报错f=open('123', ...

  4. Qemu线程池介绍

    有时我们希望把一部分工作通过创建线程的方式异步执行,这样我们可以在执行任务的同时,继续执行其他任务.但是如果这种需求比较多的话,频繁的创建和销毁线程带来很大的性能损耗.如果我们能创建一个或一些线程,然 ...

  5. Httpclient 实现带参文件上传

    这里直接贴出的是我封装好的doPostFile方法,httpclient 的版本是3.1. public static String doPostFile(String url, Part[] par ...

  6. Python内置函数之sorted()

    sorted(iterable,*,key=None,reverse=False) 对可迭代对象进行排序,默认ASCII进行排序. 例子: sorted(iterable,*,key=None,rev ...

  7. [译]GLUT教程 - 渲染到子窗体

    Lighthouse3d.com >> GLUT Tutorial >> Subwindows >> Rendering to Subwindows 先回顾一下之前 ...

  8. static 修饰的变量在程序中容易出现的问题

    package lianxi; public class StaticTest {    int a = 0;    static int b =0;    StaticTest(){         ...

  9. PHP代码中使用post参数上传大文件

    今天连续碰到了两个同事向我反应上传大文件(8M)失败的事情! 都是在PHP代码中通常使用post参数进行上传文件时,当文件的大小大于8M时,上传不能不成功. 首先,我想到了nginx的client_m ...

  10. 【Navicat Premium】之连接Oracle数据库

    1.首先,在连接之前,需要下载oracle官网提供的instantclient-basic-win32-11.2.0.1.0.zip包 官网:http://www.oracle.com/technet ...