Effective Java —— 覆盖equals时总要覆盖hashCode
本文参考
本篇文章参考自《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的更多相关文章
- Item 9 覆盖equals时总要覆盖hashCode
为什么覆盖equals时,总要覆盖hashCode? 原因是,根据Object规范: 如果两个对象根据equals(Object)方法比较是相等的,那么调用这两个对象中任意一个对象的hashCod ...
- 覆盖equals时总要覆盖hashCode
本文涉及到的概念 1.为什么重载equals方法时,要重载hashCode函数;没有重载hashCode带来的问题 2.一个对象hashCode的生成规则 1.为什么重载equals方法时 ...
- 第9条:覆盖equals时总要覆盖hashCode
在每个覆盖equals方法的类中,也必须覆盖hashCode方法.否则,会违反Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常工作,包括HashMap,Hash ...
- 【Effective Java】5、覆盖equals时总要覆盖hashcode
package cn.xf.cp.ch02.item9; import java.util.HashMap; import java.util.Map; public class PhoneNumbe ...
- 覆盖equals 时总要覆盖hashCode(9)
2019独角兽企业重金招聘Python工程师标准>>> 1.在每个覆盖了equals 方法的类中,也必须覆盖hashCode 这是关于hashCode 的通用约定 这样可以与 基于散 ...
- 覆盖equals()时总要覆盖hashCode()
覆写如下: public class User{ private Integer id; private String userName; private String passWord; publi ...
- EffectiveJava(9)覆盖equals是总要覆盖hashCode
覆盖equals是总要覆盖hashCode 通过散列函数将集合中不相等的实例均匀的分布在所有可能的散列值上 1.把某个非零的常数值保存在一个名为result的int类型变量中 2.对于对象中每个关键域 ...
- Item 8 覆盖equals时请遵守通用约定
在覆盖equals方法的时候,你必须要遵守它的通用约定,不遵守,写出来的方法,会出现逻辑错误.下面是约定的内容: equals方法实现了等价关系: 自反性.对于任何非null的引用值,x.eq ...
- 第8条:覆盖equals时遵守通用约定
如果不需要覆盖equals方法,那么就无需担心覆盖equals方法导致的错误. 什么时候不需要覆盖equals方法? 1.类的每个实例本质上是唯一的. 例如对于Thread,Object提供的equa ...
随机推荐
- 【C#基础概念】函数参数默认值和指定传参和方法参数
函数参数默认值和指定传参 最近在编写代码时发现介绍C#参数默认值不能像PL/SQL那样直接设置default,网上也没有太多详细的资料,自己琢磨并试验后整理成果如下: C#允许在函数声明部分定义默认值 ...
- 【C#基础概念】字面量 literal
一.字面量定义 在计算机科学中,字面量(literal)是用于表达源代码中一个固定值的表示法(notation).几乎所有计算机编程语言都具有对基本值的字面量表示,诸如:整数.浮点数以及字符串:而有很 ...
- java this 用法详解
一.JAVA提供了一个很好的东西,就是 this 对象,它可以在类里面来引用这个类的属性和方法. 代码例子: public class ThisDemo { String name="Mic ...
- csv 转换为DBF文件的方法
转至:https://www.cnblogs.com/hssbsw/archive/2012/12/01/2797140.html csv 转换为DBF文件的方法 最近从SQL导出了许多CSV文件发到 ...
- 深入Mybatis框架
深入Mybatis框架 学习了Spring之后,我们已经了解如何将一个类作为Bean交由IoC容器管理,也就是说,现在我们可以通过更方便的方式来使用Mybatis框架,我们可以直接把SqlSessio ...
- Java:输入输出、格式化输出
1.输出 都在System.out模块下,常用方法有: print:输出: println:输出并换行: printf:格式化输出: 2.格式化输出 格式化输出的方法是System.out.print ...
- adb常用命令大全——查看手机设备信息
查看手机型号 adb shell getprop ro.product.model 查看电池状况 adb shell dumpsys battery 其中 scale 代表最大电量,level 代表当 ...
- Linux CentOS 7.X-关机、重启命令
一.命令操作 1.退出命令 退出登陆命令:logout: 2.关闭命令 立即关机:shutdown -h now(root用户) halt poweroff 延时关机:shutdown -h m ...
- CentOS Linux服务器安装Nginx
1.安装nginx前,我们首先要确保系统安装了g++.gcc.openssl-devel.pcre-devel和zlib-devel软件,可通过如图所示命令进行检测,如果以安装我们可以通过图二所示卸载 ...
- 【漏洞复现】Paraluni 安全事件分析及复现
Paraluni 被黑分析 前言 Paraluni (平行宇宙)是新加坡 Parallel Universe 基金会发布的一个 基于币安智能链的 DeFi 项目,更多相关内容见此处.在 2022 年 ...