Java中的hashCode方法

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

hashCode()方法原理

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

equals与hashCode的约定

equals和hashCode是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方法基于firstName、lastName和age字段:

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. 不可变对象作为键

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

  • 不可变对象的哈希码不会变化,避免哈希表中的键"丢失"
  • 可变对象修改后,哈希码可能变化,导致无法正确查找
// 不推荐:使用可变对象作为键
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的返回值应基于对象的实际状态,避免使用随机数,否则相同对象可能具有不同哈希码(违法equals和hashCode的第一条约定)。

总结

  • 掌握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. slf4j、logback、log4j、log4j2的区别

    区别 slf4j是一个日志接口,自己没有具体实现日志系统,只提供了一组标准的调用api,这样将调用和具体的日志实现分离,使用slf4j后有利于根据自己实际的需求更换具体的日志系统,比如,之前使用的具体 ...

  2. java XML字符串和json字符串互转

    结果 原始 # 原始 {"head":{"opCode":"0202","bankCode":"0416000 ...

  3. Strands Agents(一)Strands Agents 介绍

    Strands Agent AWS 最新开源的 Strands Agents SDK 是一款采用模型驱动架构的 AI 代理开发框架,旨在通过极简开发方式,帮助开发者快速构建和部署 AI 代理.它将代理 ...

  4. 【附源码】C语言的学生管理系统完整实现方案

    以下是一个基于C语言的学生管理系统完整实现方案,结合了结构体.链表.文件存储.菜单驱动等核心技术,参考了多个开源项目与课程设计案例. 系统支持管理员/学生双角色权限.数据持久化存储及完整增删改查功能, ...

  5. 如何基于three.js(webgl)引擎架构,实现3D密集架库房,3D档案室智能巡检

      前言: 看了一下 距离上篇课程发布,一年多了.这一年多来也迷茫,也怀疑,在AI时代,是不是失去了写文章的意义了.切身感受到那种面对科技浪潮时的迷茫和无力感.当AI能写出流畅.结构清晰的文章,甚至模 ...

  6. 基于人类反馈的强化学习 RLHF

    1.强化学习和语言模型的联系 agent: 语言模型本身 state: prompt(input tokens) action: 选择哪个token作为下一个token(贪婪,top k,top p) ...

  7. 从“看懂世界”到“改造世界”:AI发展的四个阶段你了解了吗?

    过去几年,人工智能一路狂飙,从识图识声,到能写能画,再到能干活做决策,甚至走上现实生活的"物理战场".你是否也曾困惑,AI到底发展到哪个阶段了?又有哪些能力正在悄悄进化? Nvid ...

  8. 11.Java SDK源码分析系列笔记-Hashtable

    目录 1. 是什么 2. 如何使用 3. 原理分析 3.1. uml 3.2. 构造方法 3.3. put方法 3.3.1. 使用synchronized加锁 3.3.2. 计算key落在entry数 ...

  9. 【译】更优的模型,更智能的设置:Claude Sonnet 4、GPT-4.1 及 Visual Studio 中的更多控制

    我们很高兴宣布 Visual Studio 中 Copilot 体验的重大升级,包括更智能的默认模型.更多选择以及更简单的用量管理方式. 更智能的默认模型 Visual Studio 中的 Copil ...

  10. Layui 更新Table 表格内容的值

    $.ajax({ //请求方式 type: "POST", //请求地址 url: "/", //数据,json字符串 data: { }, //请求成功 su ...