本文参考

本篇文章参考自《Effective Java》第三版第十一条"Always override hashCode when you override equals"

You must override hashCode in every class that overrides equals

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

  • When the hashCode method is invoked on an object repeatedly during an execution of an application, it must consistently return the same value, provided no information used in equals comparisons is modified. This value need not remain consistent from one execution of an application to another
  • If two objects are equal according to the equals(Object) method, then calling hashCode on the two objects must produce the same integer result
  • If two objects are unequal according to the equals(Object) method, it is not required that calling hashCode on each of the objects must produce distinct results. However, the programmer should be aware that producing distinct results for unequal objects may improve the performance of hash tables

若未重载hashCode()方法,只是继承自Object,即使"值类"的实例"逻辑相等",也会在HashMap和HashSet集合中表现出不同,下面是原文的例子:

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

若PhoneNumber类未重载hashCode()方法,便无法使用m.get()方法获取键new PhoneNumber(707, 867, 5309)对应的值"Jenny",因为查找的过程不仅仅只是通过equals()方法比较与某个键是否相等,还比较了各自的散列码,而Object的hashCode()方法只是将实例的内存地址转化为散列码

As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the Java programming language.)

a hash function should distribute any reasonable collection of unequal instances uniformly across all int values

下面是一种解决方法:

  • Declare an int variable named result, and initialize it to the hash code c for the first significant field in your object( a significant field is a field that affects equals comparisons, and you must exclude any fields that are not used in equals comparisons)

Do not be tempted to exclude significant fields from the hash code computation to improve performance

  • For every remaining significant field f in your object, do the following:
    • If the field is of a primitive type, compute Type.hashCode(f), where Type is the boxed primitive class corresponding to f's type
    • If the field is an object reference and this class's equals method compares the field by recursively invoking equals, recursively invoke hashCode on the field. If a more complex comparison is required, compute a "canonical representation"(范式) for this field and invoke hashCode on the canonical representation. If the value of the field is null, use 0 (or some other constant, but 0 is traditional)
    • If the field is an array, treat it as if each significant element were a separate field. That is, compute a hash code for each significant element by applying these rules recursively, and combine the values. If the array has no significant elements, use a constant, preferably not 0. If all elements are significant, use Arrays.hashCode
    • Combine the hash code c
      result = 31 * result + c; (The advantage of using a prime is less clear, but it is traditional)
  • Return result

下面是采用上述规则的hashCode()重载示例

private String reference;
private int[] array;
private int primitive;
@Override
public int hashCode() {

  // Declare an int variable named result
  // and initialize it to the first significant field in your object

  int
result = reference.hashCode();

  // If all elements are significant, use Arrays.hashCode
  // Combine the hash code c

  result = 31 * result + Arrays.hashCode(array);

  // If the field is of a primitive type, compute Type.hashCode(f)

  result = 31 * result + Integer.hashCode(primitive);

  return result;
}

另外还有一种简单的生成散列码的方式 —— Objects.hash(),但是它会引发数组的创建,以便传入数目可变的参数, 如果参数中有基本类型,还需要装箱和拆箱,建议只将这类散列函数用于不太注重性能的情况

在前面一篇关于"AutoValue"的文章中,采用的是另外一套生成hashCode()方法的规则

cache the hash code in the object

如果是一个不可变的类(如"值类"),并且计算散列码开销较大(如字段较多的情况),可以将散列码作为单例缓存在对象内部,视情况采用"懒汉式"还是"饿汉式",下面代码采用"懒汉式"加载

private int hashCode;

@Override
public int hashCode() {

  int result = hashCode;

  if (result != 0) {

    result = reference.hashCode();

    result = 31 * result + Arrays.hashCode(array);

    result = 31 * result + Integer.hashCode(primitive);
  }

  return result;
}

Effective Java —— 覆盖equals时总要覆盖hashCode的更多相关文章

  1. Item 9 覆盖equals时总要覆盖hashCode

    为什么覆盖equals时,总要覆盖hashCode?   原因是,根据Object规范: 如果两个对象根据equals(Object)方法比较是相等的,那么调用这两个对象中任意一个对象的hashCod ...

  2. 覆盖equals时总要覆盖hashCode

    本文涉及到的概念 1.为什么重载equals方法时,要重载hashCode函数;没有重载hashCode带来的问题 2.一个对象hashCode的生成规则       1.为什么重载equals方法时 ...

  3. 第9条:覆盖equals时总要覆盖hashCode

    在每个覆盖equals方法的类中,也必须覆盖hashCode方法.否则,会违反Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常工作,包括HashMap,Hash ...

  4. 【Effective Java】5、覆盖equals时总要覆盖hashcode

    package cn.xf.cp.ch02.item9; import java.util.HashMap; import java.util.Map; public class PhoneNumbe ...

  5. 覆盖equals 时总要覆盖hashCode(9)

    2019独角兽企业重金招聘Python工程师标准>>> 1.在每个覆盖了equals 方法的类中,也必须覆盖hashCode 这是关于hashCode 的通用约定 这样可以与 基于散 ...

  6. 覆盖equals()时总要覆盖hashCode()

    覆写如下: public class User{ private Integer id; private String userName; private String passWord; publi ...

  7. EffectiveJava(9)覆盖equals是总要覆盖hashCode

    覆盖equals是总要覆盖hashCode 通过散列函数将集合中不相等的实例均匀的分布在所有可能的散列值上 1.把某个非零的常数值保存在一个名为result的int类型变量中 2.对于对象中每个关键域 ...

  8. Item 8 覆盖equals时请遵守通用约定

    在覆盖equals方法的时候,你必须要遵守它的通用约定,不遵守,写出来的方法,会出现逻辑错误.下面是约定的内容:   equals方法实现了等价关系:   自反性.对于任何非null的引用值,x.eq ...

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

    如果不需要覆盖equals方法,那么就无需担心覆盖equals方法导致的错误. 什么时候不需要覆盖equals方法? 1.类的每个实例本质上是唯一的. 例如对于Thread,Object提供的equa ...

随机推荐

  1. C#实现接口的两种方式:显示实现和隐式实现接口

    本示例声明一个接口IDimensions 和一个类 Box,显式实现了接口成员 GetLength 和 GetWidth. 通过接口实例 dimensions 访问这些成员. interface ID ...

  2. 7.Flink实时项目之独立访客开发

    1.架构说明 在上6节当中,我们已经完成了从ods层到dwd层的转换,包括日志数据和业务数据,下面我们开始做dwm层的任务. DWM 层主要服务 DWS,因为部分需求直接从 DWD 层到DWS 层中间 ...

  3. layout_gravity和gravity

    layout_gravity是指控件本身的位置,gravity是指控件内元素的位置.

  4. 第一次尝试3D建模和打印:色子

    打印机前几天到了,除了打印了一半自带demo之外,还没开始打过啥模型.想自己从头打印个东西.那就做个色子吧. 因为机子上有SW2020,参考这个 solidworks2017怎么建模骰子? 教程来练习 ...

  5. YUV相关积累

    关于yuv 格式-Semi Planar和Planar https://www.cnblogs.com/welen/articles/5454315.html

  6. MySQL — DCL语言

    全称 Data Control Language.数据控制语言,用来创建数据库用户.控制数据库的访问权限. 1.用户管理 1.1.查询用户 select * from user; 1.2.创建用户 - ...

  7. 关于VBS编程初步

    VBS是基于Visual Basic的脚本语言.VBS的全称是:Microsoft Visual Basic Script Edition.用记事本就可以看到源代码.在没有语法错误的前提下,直接双击左 ...

  8. pip安装使用国内源的两种方法

    pip安装后使用pip安装第三方库默认是国外源,一般安装慢连接不稳定,等得花儿都谢了,结果还告诉你安装失败..../(ㄒoㄒ)/~~ 这时我们就要想想其它办法啦,毕竟不能强求 国外不行,就只有国内了赛 ...

  9. Poco实体

    在Poco实体中,一般只有属性没有方法,这在软件设计中称为贫血模型, 在DDD领域驱动设计中,提倡充血模型,即你的Poco实体中,即有属性,也有操作属性的方法,[PS:注意这里说的是操作属性的方法,你 ...

  10. 『忘了再学』Shell基础 — 3、echo命令的介绍与使用

    目录 1.echo命令的作用 2.echo命令的基本用法 3.echo命令的-e选项用法 4.echo命令一些特殊用法 (1)输出字符带有字体颜色 (2)输出字符带有背景颜色 在讲Shell脚本之前, ...