Java hashCode()方法指南

《Java SE实战指南》16-04:hashCode()方法 -- 阅读详情

 

 

Java hashCode()方法指南

哈希是计算机学科基本概念之一。
在java中,一些常用集合都基于高效哈希算法。如HashMap、HashSet。

本文我们将重点讨论hashCode()工作原理以及如何在集合中发挥作用。

数据结构中使用hashCode()方法

一定场景下,常用集合搜索操作是非常低效的。举例,包含大量元素的list中触发线性搜索:

List<String> words = Arrays.asList("Welcome", "to", "Baeldung");
if (words.contains("Baeldung")) {
    System.out.println("Baeldung is in the list");
}
  • 1
  • 2
  • 3
  • 4

java中提供一些数据结构可以解决这类问题,如几个实现Map接口的哈希实现类。

使用哈希算法的集合通过hashCode()方法计算给定键的哈希值,然后内部使用该值去存储数据,为了查询操作更有效率。

理解hashCode() 方法工作原理

简单地说,hashCode() 方法通过哈希算法生成一个整数值。
相对的对象(通过equals方法判断)返回相同哈希值,不同对象返回不同哈希值不是必须的。

hashCode()方法的算法约定为:

  • 在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。

  • 两个相对的对象(通过equals方法判断)必须返回相同哈希值。

  • 两个不相对的对象(通过equals方法判断),调用hashCode()方法返回值不是必须不相等。但开发者需了解,不同对象返回不同的哈希值会提升效率。

实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)

本地hashCode() 方法实现

实际上有一个完全遵循上述契约的hashCode()方法实现非常简单。
为了演示,我们定义一个示例User类,它覆盖了该方法的默认实现:

public class User {

    private long id;
    private String name;
    private String email;

    // standard getters/setters/constructors

    @Override
    public int hashCode() {
        return 1;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null) return false;
        if (this.getClass() != o.getClass()) return false;
        User user = (User) o;
        return id != user.id 
          && (!name.equals(user.name) 
          && !email.equals(user.email));
    }

    // getters and setters here
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

User类提供自定义equal和hashCode方法,完全遵循各自的约定,即使hashCode方法返回任何固定值也不是非法的。

然而,这个实现完全降低哈希表的功能,每个对象将保存在相同位置,单一桶中。这时哈希表查询将执行线性搜索,完全没有带来任何优势。

改善hashCode()方法的实现

让我们稍微改善上述hashCode()方法的实现,使用User类的所有字段,为了能够让不相等的对象生成不同的结果。

@Override
public int hashCode() {
    return (int) id * name.hashCode() * email.hashCode();
}
  • 1
  • 2
  • 3
  • 4

这个基本的哈希算法肯定要比前一个好得多,因为它只是通过将名称和电子邮件字段和id的哈希代码相乘来计算对象的哈希码。
一般来说,我们可以说这是一个合理的hashCode()实现,只要我们使equals()实现与它保持一致。

标准hashCode()实现

计算哈希值的哈希算法越好,哈希表的性能就越好。下面我们讨论标准实现,使用两个素数来增加计算哈希值的唯一性。

@Override
public int hashCode() {
    int hash = 7;
    hash = 31 * hash + (int) id;
    hash = 31 * hash + (name == null ? 0 : name.hashCode());
    hash = 31 * hash + (email == null ? 0 : email.hashCode());
    return hash;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

尽管理解hashCode()和equals()方法所扮演的角色非常重要,但我们无需每次都从头实现,大多数ide都提供了生成自定义的hashCode()和equals()实现。从java 7 之后,Objects.hash() 工具方法可以方便实现:

Objects.hash(name, email)
  • 1

IntelliJ IDEA 生成下面实现:

@Override
public int hashCode() {
    int result = (int) (id ^ (id >>> 32));
    result = 31 * result + name.hashCode();
    result = 31 * result + email.hashCode();
    return result;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Eclipse生成实现如下:

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((email == null) ? 0 : email.hashCode());
    result = prime * result + (int) (id ^ (id >>> 32));
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    return result;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

除了上述基于IDE的hashCode实现,也可以通过其他工具进行有效实现,如Lombok,其maven依赖如下:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok-maven</artifactId>
    <version>1.16.18.0</version>
    <type>pom</type>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

现在User类仅需要增加@EqualsAndHashCode注解就可以实现:

@EqualsAndHashCode
public class User {
    // fields and methods here
}
  • 1
  • 2
  • 3
  • 4

类似使用 Apache Commons Lang’s HashCodeBuilder 类也可以生成 hashCode() 实现, commons-lang Maven 依赖如下:

<dependency>
    <groupId>commons-lang</groupId>
    <artifactId>commons-lang</artifactId>
    <version>2.6</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

hashCode()方法实现如下:

public class User {
    public int hashCode() {
        return new HashCodeBuilder(17, 37).
        append(id).
        append(name).
        append(email).
        toHashCode();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

一般来说,在实现hashCode()时没有通用的诀窍。强烈建议您阅读 Joshua Bloch’s Effective Java, ,它提供了实现高效散列算法的完整指南。

这里我们注意到所有这些实现都以某种形式使用数字31——这是因为31有一个很好的属性——它的乘法运算可以被比标准乘法运算快的位移所代替:

31 * i == (i << 5) - i
  • 1

处理哈希冲突

哈希表的内在行为支持这些数据结构的相关实现:即使哈希算法非常有效,两个或多个对象不相等,也有可能产生相同的哈希值。所以它们的哈希值指向相同的桶,虽然它们有不同的key值。

通常这种场景我们称为哈希冲突,存在多种方式可以处理,每个都尤其优劣。java HashMap采用单独链接方法处理冲突:

当两个或多个对象指向相同的桶,这些对象在桶中以linked List方式存储。这种情况下,哈希表是linked List的数组,每个相同哈希值对象被追加至桶的linked List中。

最坏情况下,几个桶仅绑定一个linked List,则在list中检索对象只能是线性搜索。哈希冲突说明为什么实现有效hashCode方法非常重要。

java 8 提供了HashMap的增强实现,如果桶大小超出一定阈值,会使用tree map代替 linked List,只有时间复杂度从O(n)提升至O(logn)。

创建一个示例应用

为了测试标准hashCode方法功能实现,我们创建一个简单java应用,增加一些User对象至HashMap中,并通过日志消息查看输出结果。

这里是应用程序入口:

public class Application {

    public static void main(String[] args) {
        Map<User, User> users = new HashMap<>();
        User user1 = new User(1L, "John", "john@domain.com");
        User user2 = new User(2L, "Jennifer", "jennifer@domain.com");
        User user3 = new User(3L, "Mary", "mary@domain.com");

        users.put(user1, user1);
        users.put(user2, user2);
        users.put(user3, user3);
        if (users.containsKey(user1)) {
            System.out.print("User found in the collection");
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

hashCode方法实现如下:

public class User {

    // ...

    public int hashCode() {
        int hash = 7;
        hash = 31 * hash + (int) id;
        hash = 31 * hash + (name == null ? 0 : name.hashCode());
        hash = 31 * hash + (email == null ? 0 : email.hashCode());
        logger.info("hashCode() called - Computed hash: " + hash);
        return hash;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这里唯一需要强调的细节是,每当对象存储到HashMap中并通过containsKey()方法进行检查时,都会调用hashCode()并将计算出来的哈希值打印到控制台:

[main] INFO com.dataz.entities.User - hashCode() called - Computed hash: 1255477819
[main] INFO com.dataz.entities.User - hashCode() called - Computed hash: -282948472
[main] INFO com.dataz.entities.User - hashCode() called - Computed hash: -1540702691
[main] INFO com.dataz.entities.User - hashCode() called - Computed hash: 1255477819
User found in the collection
  • 1
  • 2
  • 3
  • 4
  • 5

总结

很明显,生成高效的hashCode()实现通常需要混合一些数学概念(即素数和任意数)、逻辑和基本的数学操作。
不管怎样只要我们能确保散列算法为不相等的对象生成不同的散列码并与equals()的实现保持一致,就完全有可能实现有效的hashCode()方法。

避开ANSYS SOLID65钢筋模拟的坑:从材料TB到实常数R的完整配置清单 本文详细解析了ANSYS SOLID65单元在钢筋混凝土模拟中的关键配置与优化技巧,涵盖材料TB设置、实常数R配置及收敛性优化。特别针对钢筋建模的分布式钢筋模型进行深度解析,提供实战验证的参数设置手册,帮助工程师避免常见错误,提升模拟精度与效率。 阅读详情

相关推荐

C++实现SM4国密算法实战指南

通过以上实现,开发者可以快速在C++项目中集成SM4国密算法,满足金融、政务等领域的安全需求。实际部署时建议通过国家密码管理局的合规性检测,并定期更新密钥管理策略。库(支持国密算法),需提前安装并配置开发环境。

skyksksksksks的博客 1487

Java基础篇:什么是hashCode 以及 hashCode()与equals()的联系

1、什么是hashCode: hashCode就是对象的散列码,是根据对象的某些信息推导出的一个整数值,默认情况下表示是对象的存储地址。通过散列码,可以提高检索的效率,主要用于在散列存储结构中快速确定对象的存储地址,如Hashtable、hashMap中。 为什么说hashcode可以提高检索效率呢?我们先看一个例子,如果想判断一个集合是否包含某个对象,最简单的做法是怎样的呢?逐一取出集合中的每个元素与要查找的对象进行比较,当发现该元素与要查找的对象进行equals()比较的结果为true时,则停止继续

张维鹏的博客 6万+

ModbusRTU串口读写报文解析

解析MODBUS报文的格式,讨论异常状态、读多个寄存器与写多个寄存器

Serendipitor的博客 9209

Java 中的 hashCode

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

qq_30067153的博客 2313

Speedtree学习笔记(案例)——拥有多种材质的植物如何导入使用(以草为例)

注:Speedtree无论保存还是导入模型其存储路径不可有中文。 第一步拆分草的模型 根据所需的材质去拆分(例:假若草的果实,茎秆和叶子是不同的材质那么就需拆分为三份;假若画的全部颜色及材质一样就没有必要去拆分模型) 第二步导入草的模型 在Mesh面板中依次导入草的模型——Do nothing additional right now 第三步扩大草的生长区域范围(可省略)...

L人青的博客 5722

小白学习图像处理6——Canny边缘检测算法原理

目录结构一、高斯滤波二、梯度计算1、原理2、代码如下三、非极大值抑制三、双阈值检测四、matlab 的 edge函数五、总结 Canny边缘检测算法主要步骤:高斯滤波、梯度计算、非极大值抑制和双阈值检测。 一、高斯滤波   使用高斯滤波的目的是平滑图像,滤除图像中的部分噪声(因为微分算子对噪声很敏感)。高斯滤波具体办法是生成一个高斯模板,使用卷积进行时域滤波: img = im2double(im...

我有两颗糖 7791

Java基础之HashCode

文章目录概述1. HashCode概念1.1 HashCode定义1.2 HashCode约定1.3 常见的HashCode的生成算法2. Hash函数 (散列函数)2.1 定义2.2 特点2.3 优点2.4 常用的Hash函数其他附录 概述 介绍Java中的HashCode相关概念及其生成方法。 1. HashCode概念 1.1 HashCode定义 hashCode是jdk根据对象的地址或...

pre_tender的博客 1813

SAP PP实战:MRP Area怎么用?手把手教你按产线、委外供应商拆分物料计划

本文详细解析了SAP PP模块中MRP Area的功能与应用,指导如何按产线、委外供应商拆分物料计划,实现精细化物料需求管理。通过实战案例展示MRP Area在减少计划冲突、提升库存准确率方面的显著效果,为制造业企业提供可落地的解决方案。

weixin_27015733的博客 432

java中equals,hashcode和==的区别

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33828101的博客 814

手把手搭建你的第一个OCT仿真模型:用Python和光学仿真库重现A-SCAN信号

本文详细介绍了如何使用Python和光学仿真库搭建OCT仿真模型,重现A-SCAN信号。从环境配置、干涉仪建模到信号处理全流程,手把手教你实现光学相干层析成像的核心技术。通过代码示例和性能优化技巧,帮助开发者深入理解OCT原理并应用于医疗诊断等领域。

weixin_42562079的博客 241

(付费请教,防遗忘记录)为什么EDEM bondV1 V2添加后还是没有胶结?????

最近用到了EDEM的胶结功能,在添加了bond V1和V2后,我发现无论颗粒半径设置为多少,接触半径设置为多少,胶结键的参数设置为多少,bond generated数量均为0!!!在后处理中bond中的力也通通为0!!我跟我的同门百思不得其解!!!!最终付费请教了一个大牛,解决了这个问题!!!!答案在文末

zheaa的博客 3102

Java中正确使用equals()与hashCode()方法全面指南

Java对象相等性实践指南 本文深入探讨Java对象相等性的核心概念与实践要点: 基础概念:解析引用相等性(==)和对象相等性(equals())的区别,介绍equals()和hashCode()方法的契约要求及其协同关系 问题分析:揭示未重写equals()导致的集合操作异常、对象比较混乱、序列化问题及框架集成隐患 实现方案:提供正确重写equals()的标准步骤和不同字段类型的比较策略,特别强调继承体系中的实现注意事项 关键要点包括:遵守方法契约、保证一致性、考虑业务逻辑定义、处理继承关系等。正确实现对

本博客聚焦 YOLOv11 全流程落地,涵盖架构优化、数据集处理、训练技巧与多场景部署,兼及国产数据库、Java 开发与 AI 模型应用,内容兼顾理论与工程实践,为开发者提供系统干货,助力高效提升技术能力。 2128

C# MES通信从入门到精通(1)——串口传输文件

本文详细介绍了在Mes通信领域如何使用串口发送文件,由实战经验总结,堪称MES通信领域的葵花宝典!

qq_34059233的博客 3750

《Oracle Java SE编程指南》16-04:hashCode()方法

课程封面-JavaSE-AT阿宝哥 内容导航: 前言 1、hashCode方法 2、equals方法与hashCode方法关系 1、hashCode方法 hashCode方法用来获取对象的哈希码值。支持此方法是为了提高哈希表(例如java.util.Hashtable提供的哈希表)的性能。方法定义如下: public native int hashCode(); hashCode...

西安极客联盟 273

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Java 中的 hashCode () 与 equals () 核心原理、契约规范、重写实践与面试全解

本文是Java数据结构学习笔记系列,涵盖JAVA初阶到数据结构核心内容,包括集合、泛型、List、ArrayList、LinkedList、栈、队列、二叉树、堆等专题。重点解析了Object类中equals()和hashCode()方法的定义、实现、区别及使用规范,强调重写equals()必须同时重写hashCode()的重要性。通过HashMap源码分析,展示了这两个方法在哈希集合中的关键作用,并提供标准重写模板和IDEA生成技巧。

HRDMN的博客 428

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

java hashCode()与equals()

关于对象的hashCode()与equals()两个方法的全面解释,参考文章: https://www.cnblogs.com/skywang12345/p/3324958.html 转...

chugang1854的博客 116

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Java:重写equals()和hashCode()

以下内容总结自《Effective Java》。 1.何时需要重写equals() 当一个类有自己特有的“逻辑相等”概念(不同于对象身份的概念)。 2.设计equals() [1]使用instanceof操作符检查“实参是否为正确的类型”。 [2]对于类中的每一个“关键域”,检查实参中的域与当前对象中对应的域值。 [2.1]对于非float和double类型的原

ztianlong的专栏 499

你真的理解 equals() 和 hashCode() 吗?Map 正确使用指南

在Java中,Map的实现(如HashMap、LinkedHashMap、ConcurrentHashMap)依赖于对象的equals()和hashCode()方法来确保键的唯一性。hashCode()用于确定键应存储在哪个桶中,而equals()用于确认桶中是否存在逻辑相同的键。为了确保Map的正确行为,必须同时重写这两个方法,并遵循一致性、等价性等契约规则。未正确实现这些方法可能导致HashMap查找失败、Set去重错误等问题。自定义对象作为键时,常见错误包括未重写equals()和hashCode()

小健学Java的博客 1637

为什么重写equals()就一定要重写hashCode()方法?

如果涉及使用对象作为key,那么key的对象都必须实现hashCode和equals方法。hashCode和equals方法持续自根类Object,若是你用自定义的类算作key的话,就需要同时覆写equals办法和hashCode方法,因为我们java的集合中判断两个对象是否相等,是根据hashCode和equals这个俩个方法。规则是先判断两个对象的hashCode是否相等,如果不相等,认为两个对

sixingmiyi39473的博客 7838

Java中equals()和HashCode()的关系

前言 上一篇文章简单分析了equals()与==的关系,本文我们再来看看equals()与hashcode()的关系。hashcode的使用还是有很多坑的,一起看看吧~ 本文主要有以下几点来分析: hashCode使用中产生的问题 equals/hashcode的渊源 产生问题的原因 正确的使用姿势 hashCode使用中产生的问题 注:HashSet是一个无序、不可重复的集合,我们做一个小测...

LarsCheng's Blog 1万+

为什么要重写hashCode()方法

一.引言 首先 hashCode() 方法被设计在 Object 类中,说明是希望所有的对象都能实现该方法,该方法是会返回一个对应于当前对象的一个整数,之所以所有对象都实现该方法是为了将来对象在查找时变得快捷。 Java中的集合类 hashMap、hashSet 底层的实现都是哈希表,之所以采用哈希表,是因为他能克服链表和二叉树等数据结构在查询上的低效率问题,哈希表的存储就是根据对象的哈希值,以此确定他的存储位置。 但是,仅仅通过 hashcode() 并不能将所有对象区分开来,有可能会出现对象不同,ha

Welcome To kua_fuXz's Blog 5912

Java 中的 hashCode() 方法

本文主要介绍了Java中的hashCode方法的定义与具体实现。

专注Java和MySQL领域知识分享 1655

Java中Object的hashCode()方法

1.百度百科是这么定义的 2.声明中是这么定义的 打开声明 int java.lang.Object.hashCode() Returns a hash code value for the object. This method issupported for the benefit of hash tables such as those provided by java.util.Has...

围巢 1万+

equals()方法和hashCode()方法

str1.hashCode()和str2.hashCode()虽然是相同的,但之后str1.equals(str2)的结果却是false,因为不重写equals(),str1.equals()调用的是object的equals()方法,比较的是str1和str2的地址,这时集合中可能就会出现两个内容为hello的字符串元素。1.Object类中equals方法比较的是两个对象的引用地址,只有对象的引用地址指向同一个地址时,才认为这两个地址是相等的,否则这两个对象就不想等。

weixin_64387654的博客 1508

Java提高篇——equals()与hashCode()方法详解

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。

pleaseprintf的博客 1639

java重写equals()方法和hashCode()方法

引用块内容 1.equals()方法和hashCode()方法是什么? equals()和hashCode()都是是Java中万物之源Object类中的方法; equals方法用于比较两个对象是否相同,Object类中equals方法的实现是比较引用地址来判断的对象是否是同一个对象,通过覆盖该方法可以实现自定义的判断规则; hashCode是jdk根据对象的地址或者字符串或者数字计算...

潘超博客 2万+
上一篇: groovy 闭包实战教程
下一篇: amel的消息模型
TuxedoLinux
博客等级 码龄9年 268粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值