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. 1 MyBatis动态SQL之综述和 if 语句

    摘要:使用 MyBatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach和bind等标签,可组合成非常灵活的SQL语句 ...

  2. Spring注解之@Value注解读取配置文件属性和设置默认值

    概述   在Spring 组件中,通常使用@Value注解读取 properties 文件的配置值.但如果在配置文件或启动参数中未指定对应的参数值,则项目在启动的时候会抛出异常,导致服务启动失败,异常 ...

  3. TPS和QPS的概念

    TPS    TPS:Transactions Per Second(每秒传输的事务处理个数),即服务器每秒处理完成的事务数.TPS包括一条消息入和一条消息出,加上一次用户数据库访问.    TPS是 ...

  4. C/C++中的volatile

    C/C++中的volatile 约定 Volatile 这个话题,涉及到计算机科学多个领域多个层次的诸多细节.仅靠一篇博客,很难穷尽这些细节.因此,若不对讨论范围做一些约定,很容易就有诸多漏洞.到时误 ...

  5. 基于Fastapi的区分聊天房间的聊天转发功能接口示例

    基于房间码(eCode)和用户uid,区分不同的聊天房间进行消息转发. 前端将收到的消息根据房间码(eCode)过滤到不同的聊天记录显示页面 后端demo代码如下: from fastapi impo ...

  6. hot100之回溯下

    单词搜索(079) class Solution { int m, n; public boolean exist(char[][] board, String word) { m = board.l ...

  7. CAE技术的局限性讨论-CAE咨询

    计算机辅助工程(Computer-Aided Engineering, CAE)技术是现代工程领域中不可或缺的一部分,它通过数值模拟和计算分析,为工程设计和优化提供了强大的工具.然而,就像任何技术一样 ...

  8. java set TreeSet详解

    TreeSet 是sortedSet的唯一实现类,正如SortedSet名字暗示,TreeSet可以让集合元素处在排好序的状态. 与HashSet相比,TreeSet还额外提供了以下的方法(列出来,混 ...

  9. Visual Studio 2022 官网快捷键

    键盘快捷方式 - Visual Studio (Windows) | Microsoft Docs Visual Studio 中的键盘快捷方式 可打印快捷方式备忘单 单击可获取适用于 Visual ...

  10. java下载文件写的工具类

    netUrl:提供一个文件的网址 filePath:本地保存的路径 1 ... 2 private File getNetUrlHttp(String netUrl, String filePath) ...