如何写通用的Java hashCode

为什么覆写equals的时候一定要覆写hashCode? 经常在论坛上面看到复写hashCode函数的问题,很多情况下是一些开发者不了解hash code,或者和equals一起用的时候不太清楚为啥一定要复写hashCode。对于hash code的理论我不想多说,这个话题太大。我只想说用hash code的原因只有一个:效率。理论的说法它的复杂度只有O(1)。试想我们把元素放在线性表里面,每次要找一个元素必须从头一个一个的找它的复杂度只有O(n)。如果放在平衡二叉树,复杂度也有O(log n)。接下来说为啥很多地方说“覆写equals的时候一定要覆写hashCo 阅读详情

Google首席Java架构师Joshua Bloch在他的著作《Effective Java》中提出了一种简单通用的hashCode算法

1. 初始化一个整形变量,为此变量赋予一个非零的常数值,比如int result = 17;

2. 选取equals方法中用于比较的所有域,然后针对每个域的属性进行计算:

  (1) 如果是boolean值,则计算f ? 1:0

  (2) 如果是byte\char\short\int,则计算(int)f

  (3) 如果是long值,则计算(int)(f ^ (f >>> 32))

  (4) 如果是float值,则计算Float.floatToIntBits(f)

  (5) 如果是double值,则计算Double.doubleToLongBits(f),然后返回的结果是long,再用规则(3)去处理long,得到int

  (6) 如果是对象应用,如果equals方法中采取递归调用的比较方式,那么hashCode中同样采取递归调用hashCode的方式。  否则需要为这个域计算一个范式,比如当这个域的值为null的时候,那么hashCode 值为0

  (7) 如果是数组,那么需要为每个元素当做单独的域来处理。如果你使用的是1.5及以上版本的JDK,那么没必要自己去    重新遍历一遍数组,java.util.Arrays.hashCode方法包含了8种基本类型数组和引用数组的hashCode计算,算法同上,

  java.util.Arrays.hashCode(long[])的具体实现:

public static int hashCode(long a[]) {
        if (a == null)
            return 0;

        int result = 1;
        for (long element : a) {
            int elementHash = (int)(element ^ (element >>> 32));
            result = 31 * result + elementHash;
        }

        return result;
}

Arrays.hashCode(...)只会计算一维数组元素的hashCOde,如果是多维数组,那么需要递归进行hashCode的计算,那么就需要使用Arrays.deepHashCode(Object[])方法。

3. 最后,要如同上面的代码,把每个域的散列码合并到result当中:result = 31 * result + elementHash;

4. 测试,hashCode方法是否符合文章开头说的基本原则,这些基本原则虽然不能保证性能,但是可以保证不出错。

 

 为什么每次需要使用乘法去操作result? 

主要是为了使散列值依赖于域的顺序,还是上面的那个例子,Test t = new Test(1, 0)跟Test t2 = new Test(0, 1), t和t2的最终hashCode返回值是不一样的。

 为什么是31? 31是个神奇的数字,因为任何数n * 31就可以被JVM优化为 (n << 5) -n,移位和减法的操作效率要比乘法的操作效率高的多。具体解释为:

看一看 String hashCode 方法的源码:

/**
     * Returns a hash code for this string. The hash code for a
     * {@code String} object is computed as
     * <blockquote><pre>
     * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
     * </pre></blockquote>
     * using {@code int} arithmetic, where {@code s[i]} is the
     * <i>i</i>th character of the string, {@code n} is the length of
     * the string, and {@code ^} indicates exponentiation.
     * (The hash value of the empty string is zero.)
     *
     * @return  a hash code value for this object.
     */
    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

可以从注释看出:空字符串的 hashCode 方法返回是 0。并且注释中也给了个公式,可以了解了解。

  String 源码中也使用的 31,然后网上说有这两点原因:

原因一:更少的乘积结果冲突

  31是质子数中一个“不大不小”的存在,如果你使用的是一个如2的较小质数,那么得出的乘积会在一个很小的范围,很容易造成哈希值的冲突。而如果选择一个100以上的质数,得出的哈希值会超出int的最大范围,这两种都不合适。而如果对超过 50,000 个英文单词(由两个不同版本的 Unix 字典合并而成)进行 hash code 运算,并使用常数 31, 33, 37, 39 和 41 作为乘子,每个常数算出的哈希值冲突数都小于7个(国外大神做的测试),那么这几个数就被作为生成hashCode值得备选乘数了。

  所以从 31,33,37,39 等中间选择了 31 的原因看原因二。

原因二:31 可以被 JVM 优化

  JVM里最有效的计算方式就是进行位运算了:

  * 左移 << : 左边的最高位丢弃,右边补全0(把 << 左边的数据*2的移动次幂)。
  * 右移 >> : 把>>左边的数据/2的移动次幂。
  * 无符号右移 >>> : 无论最高位是0还是1,左边补齐0。   

       所以 : 31 * i = (i << 5) - i(左边  31*2=62,右边   2*2^5-2=62) - 两边相等,JVM就可以高效的进行计算啦。。。

另外,在Objects类中,还可以使用hash方法一次性传多个参数,分别为各个参数调用不同的Objects.hashCode方法,比如:

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

插播一下,利用lombok项目提供的注解功能,能有效的解决这种重复的基础工作。只需要两步。

添加lombok项目的依赖

<dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.16.20</version>
 </dependency>

添加注解:@Data 和 @EqualsAndHashCode

import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode(exclude = {"score","time"})
public class EqualsDemo {
    private String name;
    private int age;
    private boolean gender;
    private float score;
    private String time;
}

另外,可以看一下 Objects的hash()和hashCode()比较:https://blog.youkuaiyun.com/weixin_41050155/article/details/83029350

 

如何正确重写hashCode方法? 以下是李大佬的回答作者李子捌链接https那为什么是31呢?31为什么能成为JDK计算团队选中的真命天子,就不能是2?不能是1001?其实使用31作为乘积因子是有原因的,其原因小捌觉得有三点31是一个不大不小的数,它不会过小导致hashcode计算的结果容易发生冲突;因为返回值是一个int整数类型也不至于过大,导致hashcode返回值溢出。31是一个奇数,一个数与奇数相乘,不容易丢失低位;...... 阅读详情

相关推荐

覆写equals 方法必须覆写hashCode方法

package com.zcw1.demo46; import lombok.Data; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @program: 151 * @description: 测试类 * @author:...

知识改变命运,ヾ(◍°∇°◍)ノ゙【求关注】 371

hashCode()应该怎么写才能高效?

对于散列表,高效的hash函数是保证速度的关键。在java中每个对象都有自己的hashcode()函数,但hashcode()怎么写才高效呢?以下是jdk7系统类库的写法。 String类 /** * Seed value used for each alternative hash calculated. */ private static final int H

平僧的专栏 1871

equals与hashCode在实际开发中的重写写法

NULL 博文链接:https://alpha2009.iteye.com/blog/570322

JAVA中hashCode的编写

1、  把某个非零常数值,比如17,保存在一个叫result的int类型的变量中。2、  对于对象中的关键域f(指equals方法中考虑的每一个域),完成以下步骤:A、为该域计算int类型的散列码c:I、               如果该域是boolean类型,则计算(f ? 0 : 1)。II、            如果该域是byte、char、short或者int类型,则计算(int)f。...

beyondqinghua的专栏 422

WPF的基础设施:XAML基础语法

XAML是WPF的核心标记语言,采用XML语法实现界面与逻辑分离。其基础语法包括:1)控件声明方式(特性语法和属性元素语法);2)x:Name与Name的区别(适用范围和编译行为);3)注释规范与代码折叠技巧;4)实用特性如属性继承、附加属性等。文章还提供了常见错误排查方法,建议通过实践练习掌握XAML开发技巧。全文系统介绍了XAML的核心语法与应用场景,是WPF开发者的基础学习指南。

Hare的博客 1755

Tomcat漏洞——弱口令爆破

靶机:CentOS7 IP:192.168.100.40。攻击机:kali IP:192.168.100.60。版 权 声 明:本博客所有文章除特别声明外,均采用。文章版权归作者所有,未经允许请勿转载!作 者:PeiyuJue。

weixin_57379923的博客 909

Java 中的 hashCode

所以,如果你的 hashCode() 方法依赖于对象中易变的数据,用户就要当心了,因为此数据发生变化时,hashCode() 就会生成一个不同的哈希值,相当于产生了一个不同的键。原因就在于重写 equals() 方法的时候没有重写 hashCode() 方法,而在默认情况下,hashCode() 方法是一个本地方法,会返回对象的存储地址,而 put() 中的 employee1 和 map.get(employee2) 中的对象是两个对象,所以它们的存储地址一定是不同的。

qq_30067153的博客 2313

【理解机器学习算法】之Clustering算法(Agglomerative Clustering)

是一种层次聚类算法,通过逐步合并或“聚集”它们来构建嵌套聚类。这种方法采用自底向上的方式构建聚类层次:它从将每个数据点作为单个聚类开始,然后迭代合并最接近的聚类对,直到所有数据点合并为一个聚类,或直到达到指定的聚类数量。优点:- 聚合聚类在距离和连接准则的选择上具有多样性,使其适用于各种数据集。- 它产生了一个层次结构,这对于不同规模的聚类数据结构是有信息量的。-缺点:- 对于大数据集来说,它可能计算成本高,因为它需要在每次迭代中计算和更新所有聚类对之间的距离。

Hyman Qiu 2688

java hashCode()方法

hashCode()方法 返回对象的哈希码值。 哈希值根据对象的地址或字符串或数字使用hash算法计算出来的int类型的数值。 一般情况下,相同的对象返回相同的哈希码。

卓汶的博客 197

Synopsys SCL许可证服务器搭建详解:从安装到生成.dat文件

本文详细解析了Synopsys SCL许可证服务器的完整搭建流程,从系统环境准备、SCL安装到生成.dat文件。重点阐述了如何正确获取HOST ID、配置License文件以及调试常见故障,为数字芯片设计工程师提供了一套从零到一的实战避坑指南,确保EDA工具链稳定运行。

weixin_29306011的博客 611

PMO与供应链管理的整合:端到端项目交付的优化策略

在当今全球化和市场竞争日益激烈的环境中,组织面临着前所未有的项目复杂性和交付压力。传统上相互独立的项目管理办公室(PMO)与供应链管理(SCM)正逐渐意识到:只有通过无缝整合,才能实现真正高效的端到端项目交付。本文深入探讨了PMO与供应链管理整合的战略价值、核心框架和实施路径,通过生动的比喻、实际案例和实用工具,展示了如何打破部门壁垒,构建协同高效的项目生态系统。无论您是PMO负责人、供应链专业人士还是高级管理层,都将从本文获得重塑项目交付模式、优化资源利用、降低风险并提升组织竞争力的宝贵见解和行动指南。

项目管理的博客 947

Effective Java 3rd 条目11 当你覆写equals时每次都覆写hashCode

在每个覆写equals的类中,你必须覆写hashCode。如果你不这么做,你的类将违反hashCode的通用协定,这将阻止它在数据集(比如HashMap和HashSet)中正常运行。以下是这个协定,改编自Object的文档: 在应用执行期间,当在一个对象上反复调用hashCode方法时,它必须一致返回同样的值,假设equals比较中使用的信息没有改变。当一个应用的执行到另外一个应用, 这个值没必要保

tigershin的专栏 565

Python 爬虫教程:财经新闻实时抓取(最新资讯 + 关键词提取)

在当今信息爆炸的时代,财经新闻的实时获取对于投资者、研究人员以及普通大众来说都具有重要的价值。通过 Python 爬虫技术,我们可以高效地抓取财经新闻,并提取其中的关键信息,以便更好地进行数据分析和决策支持。本文将详细介绍如何使用 Python 编写一个财经新闻实时抓取系统,并结合关键词提取技术,帮助读者快速掌握 Python 爬虫的核心技能。

yansideyucsdn的博客 1049

只要覆写equals,就必须覆写hashCode

只要覆写equals,就必须覆写hashCod 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。 新的改变 我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写...

weixin_46200547的博客 2172

Vue3 - 实现用户上传本地视频文件到服务器+上传进度条+暂停续传+中断取消上传等,视频上传后可在线播放预览已上传的视频,视频文件上传视频回显示例代码(支持Element、Antd等upload组件

实现视频上传和视频预览,vue3实现视频上传功能,上传视频回显,视频流上传播放功能,vue与axios上传视频并显示上传进度,upload组件实现视频文件上传视频回显,upload组件实现视频上传及进度条展示,视频上传及实时进度条显示,视频上传过程中如何中断上传,VUE3+element 上传视频,大文件分片上传+断点续传,axios大文件切片上传,Vue3上传视频后如何播放预览,上传本地视频到后端服务器及在线预览播放视频+移除视频+进度条上传,将已选视频上传服务端并可预览视频,详细实现选择视频并上传到服务

🏆 前端领域优质创作者 3114

基于YOLOv8的应急通道占用监测系统设计与实现

通过详细的数据集选择、模型训练、代码实现等内容,本博客为您提供完整的系统搭建过程,帮助您构建高效的监测系统。然而,在一些建筑物和公共场所中,应急通道经常被不当占用,导致紧急情况下无法及时使用,严重影响人员的疏散效率。YOLOv8是YOLO系列中的最新版本,它通过引入深度学习的优化和改进,在保证检测速度的同时,显著提升了检测精度。同时,随着深度学习技术的不断发展,检测精度与速度的提升将使得该系统在更广泛的场景中应用。:如果已有监控设备,可以采集应急通道的实时视频,并进行标注,生成占用监测数据集。

YOLO 797

java中hashcode和equals的区别和联系

理解: 规范1:若重写equals(Object obj)方法,有必要重写hashcode()方法,确保通过equals(Object obj)方法判断结果为true的两个对象具备相等的hashcode()返回值。说得简单点就是:“如果两个对象相同,那么他们的hashcode应该 相等”。不过请注意:这个只是规范,如果你非要写一个类让equals(Object obj)返回true而hashco...

qq_42273599的博客 7401

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Java中hashCode原理

哈希算法又称摘要算法,核心在于有一个hash函数,将输入映射到一个固定位数的值,这个值就叫哈希值或哈希码(HashCode)。哈希的目的在于将原本复杂、不规则的数据转化为简洁的、固定长度的值,使得数据的存储和检索更加高效。广泛应用于数据存储(如哈希表)、数据完整性验证(如MD5、SHA)、密码学(加密)以及区块链等领域。1、Java中默认hashcode()实现是基于Xorshift的伪随机数生成算法,受到当前线程有关的一个随机数+三个确定值影响,内容相同的对象,每次new对象,哈希值都不同。

little_boy_lm的博客 1917

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Java对象的hashCode和equals方法

前言 根据阿里《Java开发手册》,对 Java 对象的hashCode和equals方法,有如下强制约定。 [强制] 关于hashCode和equals的处理,遵循如下规则 1)只要覆写 equals,就必须覆写 hashCode。 2)因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须覆写这两个方法。 3)如果自定义对象作为 Map 的键,那么必须覆写 hashCode 和 equals。 说明:String ...

yangjnsjbad的博客 605

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

常常被忽略的hashCode方法

常常被忽略的hashCode方法 (一)概述 我们在编写Java程序时,一定会发现Object对象中有一个奇怪的方法:hashCode方法。我们平时很少会主动去调用这个方法,甚至可能有人完全没听说过这个方法。那么这个方法是干什么用的呢?我们可以来看官方文档对他的解释: hashCode通用约定: 调用运行Java应用程序中的同一对象,hashCode方法必须始终返回相同的整数。这个整数不需要在不同的Java应用程序中保持一致。 根据equals(Object)的方法来比较,如果两个对象是相等的,两个对象调

物是人非,结局始终不美 2155

effective-java 学习笔记 覆写hashCode

覆写hashCode规则任何class如果覆写了equals()方法,就必须覆写hashCode(),负责你的类就不能很好的与java的集合框架协同工作。在程序执行期间,同一个对象调用hashCode()必须返回同一个值(同一个应用执行期)如果两个对象equals,那么他们的hashCode()必须相等如果两个对象equals不相等,那么他们的hashCode()不必产生不同的结果,程序员应该注意

JAVA 4833

通用的Java hashCode重写方案

Google首席Java架构师Joshua Bloch在他的著作《Effective Java》中提出了一种简单通用的hashCode算法 1. 初始化一个整形变量,为此变量赋予一个非零的常数值,比如int result = 17; 2. 选取equals方法中用于比较的所有域,然后针对每个域的属性进行计算:   (1) 如果是boolean值,则计算f ? 1:0   (

一点一滴 315

Effective Java 对于所用对象都通用的方法 9.覆盖equals时总要覆盖hashCode

相信在使用快捷键覆盖equals方法时,总会伴随着hashCode,很多初学者不知道equals跟hashCode在实体类中的作用,我初学时也不知道。使用的比较多的就是字符串调用equals方法,但是跟实体类中的equals方法没有半毛钱关系。正是因为覆盖equals方法时,会覆盖hashCode,所以就没有遇到过其中的错误。每个覆盖了equals方法的类中,也必须覆盖hashCode方法。这属于乌

yancyang的博客 2万+

hashCode

简要介绍hashCode引入的原因及使用原则

云原生Java Web领域技术博客 945
上一篇: MapReduce优化: Combiner和Partitioner
下一篇: jsonpath 解析json内容实战
大树叶
博客等级 码龄12年 1171粉丝 664原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值