Java中的hashCode方法

在Java集合框架中,哈希表(如HashMap、HashSet)凭借其高效的查找性能成为存储键值对的首选数据结构。而哈希表的高效运作,离不开hashCode()

hashCode()方法原理

hashCode方法通过哈希算法生成一个整数值(哈希码),用于标识对象的哈希值。它在哈希表中的核心作用是快速定位对象的存储位置,从而实现O(1)级别的平均查找效率。

equals与hashCode的约定

equalshashCode是Java中两个紧密关联的方法,正确实现它们必须遵循以下约定:

  1. 在对象的equals比较所使用的信息未修改的情况下,多次调用hashCode()必须返回相同的值
  2. 如果两个对象通过equals()方法判断为相等,则它们的hashCode()必须返回相同的值
  3. 如果两个对象通过equals()方法判断为不相等,它们的hashCode()最好返回不同的值(但不是必须)

hashCode()工作流程

  1. 当插入键值对时,通过key.hashCode()计算哈希值
  2. 根据哈希值计算存储位置(桶索引)
  3. 将键值对存储在对应桶中
  4. 查找时,通过相同的哈希值快速定位到可能的桶,再通过equals()精确匹配

哈希冲突

当不同对象的hashCode()返回相同值时,会发生哈希冲突。哈希表通常采用"链地址法"处理冲突:将同一桶中的元素以链表形式存储。

频繁的哈希冲突会导致链表过长,使哈希表的查找性能从O(1)退化到O(n)。Java 8对HashMap进行优化:当链表长度超过阈值时,自动转换为红黑树,将时间复杂度优化为O(log n)。

正确实现hashCode方法

实现hashCode的核心原则是:equals方法中用于比较的所有字段,都必须参与hashCode的计算

Person类为例,假设其equals方法基于firstNamelastNameage字段:

import java.util.Objects;

class Person {
private String firstName;
private String lastName;
private int age; @Override
public int hashCode() {
// 基于所有参与equals比较的字段计算哈希码
int result = Objects.hashCode(firstName);
result = 31 * result + Objects.hashCode(lastName);
result = 31 * result + age;
return result;
}
}

使用Objects.hash简化实现

Java提供了Objects.hash方法,可以简化hashCode的实现:

@Override
public int hashCode() {
return Objects.hash(firstName, lastName, age);
}

Objects.hash内部会处理null值(对null字段返回0),并使用与手动实现类似的算法计算哈希码。

为什么使用31作为乘数?

在手动实现hashCode时,常使用31作为乘数,原因是:

  1. 31是一个质数,能减少哈希冲突
  2. 31 = 2^5 - 1,JVM可将乘法优化为位运算(31 * i = (i << 5) - i),提高计算效率

实际应用中的注意事项

1. 不可变对象作为键

最好使用不可变对象(如StringInteger)作为哈希表的键,原因是:

  • 不可变对象的哈希码不会变化,避免哈希表中的键"丢失"
  • 可变对象修改后,哈希码可能变化,导致无法正确查找
// 不推荐:使用可变对象作为键
class MutableKey {
private String value;
// setter方法允许修改value
public void setValue(String value) {
this.value = value;
}
// equals和hashCode基于value实现
}

2. 缓存哈希码

对于计算成本高的hashCode,可以缓存结果提高性能:

class ComplexObject {
private String data;
private int hashCode;
private boolean hashCodeComputed; @Override
public int hashCode() {
if (!hashCodeComputed) {
hashCode = Objects.hash(data);
hashCodeComputed = true;
}
return hashCode;
}
}

3. 避免使用随机值

hashCode的返回值应基于对象的实际状态,避免使用随机数,否则相同对象可能具有不同哈希码(违法equalshashCode的第一条约定)。

总结

  • 掌握hashCode的作用、原理(约定)和工作流程
  • 了解哈希冲突和解决方式(后续单独讨论)
  • 掌握hashCode的正确实现,所有参与equals比较的字段都必须参与计算
  • 掌握Objects.hash()方法来简化实现

Java集合——5.编写hashCode方法的更多相关文章

  1. Java 集合系列 14 hashCode

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  2. java中equals和hashCode方法随笔二

    前几天看了篇关于java中equals和hashCode方法的解析 1.Object类中的equals方法和hashCode方法. Object类中的equals和hashCode方法简单明了,所有的 ...

  3. Java equals()方法和hashCode()方法

    equals()方法 如果满足了以下任何一个条件,就不需要覆盖equals()方法: 1 类的每个实例本质上都是唯一的. 2 不关心类是否提供了“逻辑相等”的测试功能. 3 父类已经覆盖了equals ...

  4. java重写equals和hashCode方法

    一.重写equals方法 如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等. 利用equals比较八大包装对象(如int,f ...

  5. Day09_43_Set集合_HashSet_02(HashCode方法 与 equals方法 )

    HashSet - 向Hash表中添加元素的过程? 1. 先调用将要被存储的值key的HashCode方法得出Hash值,如果该Hash值在现有Hash表中不存在,那么直接加入元素. 2. 如果该Ha ...

  6. 【Java】equals()与hashCode()方法详解 (转)

    java.lang.Object类中有两个非常重要的方法: 1 2 public boolean equals(Object obj) public int hashCode() Object类是类继 ...

  7. 廖雪峰Java5集合-2List-2编写equals方法

    List是一种有序链表: List内部按照放入元素的先后顺序存放 每个元素都可以通过索引确定自己的位置 boolean contains(Object o) 是否包含某个元素 int indexOf( ...

  8. Java 重写equals()与hashCode()方法

    List对象的contains方法实际上也是调用的equals()方法来进行逐条对比的. 示例代码: package com.imooc.collection; /** * 课程类 */ public ...

  9. java中equals和hashCode方法的解析

    解析Java对象的equals()和hashCode()的使用 前言 在Java语言中,equals()和hashCode()两个函数的使用是紧密配合的,你要是自己设计其中一个,就要设计另外一个.在多 ...

  10. java集合_collection 中的方法 通过Arraylist来体现

    import java.util.*; /* Collection定义了集合框架的共性功能.1,添加    add(e);    addAll(collection); 2,删除    remove( ...

随机推荐

  1. Intellij IDEA插件Free Mybatis plugin

    Free Mybatis plugin这个Intellij IDEA里面的插件真的十分nice,不仅可以实现XML文件与Mapper接口相互跳转,还可以逆向生成mapper和类. 直接在IDEA插件搜 ...

  2. cmake 简易教程

    CMake 是一个跨平台的.开源的构建工具.cmake 是 makefile 的上层工具,它们的目的正是为了产生可移植的makefile,并简化自己动手写makefile时的巨大工作量,目前很多开源的 ...

  3. Predixy的docker化

    概述 当前已有一套redis cluster的集群,但是fs中的hiredis只能配置单实例redis. AI了一下方案,可以使用redis的proxy组件来实现从hiredis到redis clus ...

  4. 浅析区块链BaaS平台定位

    一.概述 区块链是一种聚合了分布式存储.密码学.链式结构.p2p通讯,使得链上数据具有防篡改.可追溯等特点的一种信任技术. 继数字藏品热潮之后,2023年以来,区块链的发展进入了Gartner成熟度曲 ...

  5. 深入浅出Flink CEP丨如何通过Flink SQL作业动态更新Flink CEP作业

    复杂事件处理(CEP)是一种对事件流进行分析的技术,它能够识别出数据流中的事件序列是否符合特定的模式,并允许用户对这些模式进行处理.Flink CEP 是 CEP 在 Apache Flink 中的具 ...

  6. 高效存储的秘诀:bitmap 数据结构在标签中的应用

    在当今大数据和信息爆炸的时代,如何有效地管理和查询海量的数据成为了企业和开发者面临的重大挑战.其中,标签系统作为数据管理中的一种重要手段,被广泛应用于用户画像.商品分类.内容推荐等多个场景.然而,随着 ...

  7. 终于有人把不同标签的加工内容与落库讲明白了丨DTVision分析洞察篇

    上一篇文章详细给大家介绍了标签的设计与加工,在标签生命周期流程中,标签体系设计完成后,便进入标签加工与上线运行阶段,一般来说数据开发团队会主导此过程,但我们需要关心以下几个问题: ·标签如何快速创建和 ...

  8. Selenium框架

    Selenium框架 Selenium是一个自动化测试工具,用于模拟用户在Web应用程序上的操作.它提供了多种编程语言的接口,如Python.Java等,使测试人员能够编写自动化测试脚本.Seleni ...

  9. jsrpc+mitmproxy联动burp实现加密自动化

    前言 在测试中发现,数据包中存在一个签名字段,将请求体进行修改后,服务器会302跳转到登录页 创建Jsrpc连接 根据关键字找到sign生成位置,并发现其加密的参数为请求体中"&se ...

  10. STL:迭代器与常用算法

    迭代器 C++ STL(Standard Template Library,标准模板库)中迭代器与常用算法是泛型编程的核心组成部分.它们配合使用,可以对容器进行高效.统一的操作.下面是对它们的系统性总 ...