本文参考

本篇文章参考自《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. 截图工具snipaste

    下载地址: https://zh.snipaste.com/download.html 使用: 按F1截图,截图后按F3悬浮

  2. 五、ES6之对象

    一.对象和属性和方法 JavaScript中对象: var person={name:"Jack",age:20}; 或: var name = "jack"; ...

  3. 反射、静态代理、动态代理(jdk、cglib)

    一.反射 反射在之前的文章中详细的解释过了,简单概括就是:可以动态的获取到一个类内部的所有的信息,动态的去创建对象和使用对象以及可以操作对象的属性和方法. 二.代理 首先解释一下代理:使用一个代理对象 ...

  4. GPT-3被超越?解读低能耗、高性能的GlaM模型

    原创作者 | LJ GLaM: Efficient Scaling of Language Models with Mixture-of-Experts https://arxiv.org/pdf/2 ...

  5. linux作业--第八周

    1.创建私有CA并进行证书申请. 配置文件存放路径 /etc/pki/tls/openssl.cnf [ CA_default ] dir = /etc/pki/CA # Where everythi ...

  6. LGP6240题解

    题解 我们可以发现,背包有结合律. 也就是先加入元素 \(a\) 再加入元素 \(b\) 和 \(c\),与先加入元素 \(a\) 后再与只有元素 \(b\) 和元素 \(c\) 的背包合并,得到的背 ...

  7. linux 常用命令:

    查看启动进程命令 ps -ef |  grep donet 1.vi 进入编辑界面 2.ls,ll 查看命令 3.cd 进入命令 4.i 进入插入界面 5. esc 退出编辑界面 6.输入:冒号,进入 ...

  8. ZYNQ使用ymodem协议传输文件

    SDK: V2014.4 协议: Ymodem 工具: USB转UART转接线.xshell6软件 可实现各种文件传输,大小不限,只是速度很慢 参考原代码如下: /****************** ...

  9. java中对集合操作的易错点01

    今天用for循环遍历集合,对集合中满足条件的元素进行remove操作报错:ConcurrentModificationException 所以,在遍历集合进行增.删操作时,要使用迭代器的方式 publ ...

  10. 在使用Access连接后获取数据--出现此类问题如何解决---未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序解决办法

    转载:https://blog.csdn.net/yyzzhc999/article/details/79367114 using System; using System.Collections.G ...