当前位置: 首页 > news >正文

深入解析Java HashCode计算原理 少看大错特错的面试题

文章目录

  • 深入解析Java HashCode计算原理 少看大错特错的面试题
    • 前言
    • HashCode 出处
      • String HashCode
        • char数组得值讲述
        • hashCode计算过程
      • Integer HashCode
      • Long HashCode
      • 类 hashCode

深入解析Java HashCode计算原理 少看大错特错的面试题

前言

首先了解这一篇文章我们先要知道 HashCode是干什么得,才能深入得了解HashCode!
hashCode方法可以这样理解:它返回的就是根据对象的内存地址换算出的一个值。这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置。 这段话来自面试题中得讲解,想必很多人都看过,这句话讲的是不对的,那么本篇主要讲述得内容就是HashCode 计算得过程。

HashCode 出处

通过前言应该也大概明白了HashCode 是干什么得了,那么接下来我讲解一下这个HashCode得出处。
估计大家都知道map一定是会使用HashCode得,这里决定调用谁谁谁得hashCode由Map得kay得类型而决定,众所周知map不能使用基本数据类型 这里要搞清楚 Java得封装数据类型是对基本数据类型得封装。当map kay得类型 如String Long 或者是自定义类得时候 只要父类重写了hashCode 那么调用得就是调用的是重写得那个hashCode,没有重写HashCode 调用的是 Object 类的 hashCode(),这是 native 方法(由 C/C++ 实现),Java 调试器无法进入 native 方法。

String HashCode

底层数组存储介绍
在这里插入图片描述

这里我们要清楚我们不用过多关心这个我赋值得时候明明是字符串 为什么这里怎么debug都debug不到它变成了char数组了呢 或者是1.8以后得byte数组了呢 ,这个转换得过程我们不用过多的关心 字符串字面量(如 “1”)转换为 char 数组的过程并不是由 String 类的 Java 源码直接实现的,而是由 Java 编译器(javac) 和 类加载机制 共同完成的,最终通过 String 类的特殊构造逻辑初始化 char value[] 数组。
若要搞清楚这个String底层得原理和加载机制 后续我会再写一篇关于String 从启动到使用过程得底层讲解。

char数组得值讲述
 public static void main(String[] args) throws Exception {String a = "1233";// 显示当前默认字符集Charset defaultCharset = Charset.defaultCharset();System.out.println("当前默认字符集: " + defaultCharset.displayName());System.out.println("字符集别名: " + String.join(", ", defaultCharset.aliases()));System.out.println("字符集编码: " + defaultCharset.name() + "\n");// 通过反射获取String类的value字段Field valueField = String.class.getDeclaredField("value");valueField.setAccessible(true); // 允许访问私有字段// 获取字符串对象的字符数组char[] value = (char[]) valueField.get(a);// 输出数组长度System.out.println("char数组长度: " + value.length);// 遍历数组,输出每个字符及其Unicode码for (int i = 0; i < value.length; i++) {System.out.printf("索引 %d: 字符='%c'  Unicode码=0x%04X (%d)%n",i, value[i], (int) value[i], (int) value[i]);}
//todo 执行结果
//        当前默认字符集: UTF-8
//        字符集别名: unicode-1-1-utf-8, UTF8
//        字符集编码: UTF-8//        char数组长度: 3
//        索引 0: 字符='1'  Unicode码=0x0031 (49)
//        索引 1: 字符='2'  Unicode码=0x0032 (50)
//        索引 2: 字符='3'  Unicode码=0x0033 (51)System.out.printf("hashCode:%d", a.hashCode());}    public static void main(String[] args) throws Exception {String a = "1233";// 显示当前默认字符集Charset defaultCharset = Charset.defaultCharset();System.out.println("当前默认字符集: " + defaultCharset.displayName());System.out.println("字符集别名: " + String.join(", ", defaultCharset.aliases()));System.out.println("字符集编码: " + defaultCharset.name() + "\n");// 通过反射获取String类的value字段Field valueField = String.class.getDeclaredField("value");valueField.setAccessible(true); // 允许访问私有字段// 获取字符串对象的字符数组char[] value = (char[]) valueField.get(a);// 输出数组长度System.out.println("char数组长度: " + value.length);// 遍历数组,输出每个字符及其Unicode码for (int i = 0; i < value.length; i++) {System.out.printf("索引 %d: 字符='%c'  Unicode码=0x%04X (%d)%n",i, value[i], (int) value[i], (int) value[i]);}//todo 执行结果
//        当前默认字符集: UTF-8
//        字符集别名: unicode-1-1-utf-8, UTF8
//        字符集编码: UTF-8//        char数组长度: 3
//        索引 0: 字符='1'  Unicode码=0x0031 (49)
//        索引 1: 字符='2'  Unicode码=0x0032 (50)
//        索引 2: 字符='3'  Unicode码=0x0033 (51)
//        索引 3: 字符='3'  Unicode码=0x0033 (51)System.out.printf("hashCode:%d", a.hashCode());}

通过上述代码我们进行开始讲解至于整个过程为什么会识别为UTF-8则是指定优先使用 JVM 启动参数 指定的字符集 若未指定,则根据 操作系统的默认编码 和 区域设置(Locale) 自动推断(例如 Windows 系统默认可能是 GBK,Linux/macOS 通常是 UTF-8)。
通过上述代码我们已经获取到了49,50,51,51。Java 底层把每个字符都拆分开来了 获取到了数组中得三个值
那么这个49,50,51,51则是把字符串转换为十进制得到得结果 就是这个49,50,51,51了。

hashCode计算过程

在这里插入图片描述

通常我们在这里调试得时候我们会看到这里是要循环value数组得值进行计算。
在这里插入图片描述

通过这张图片我们能看到 每个值都有对应的十进制得值。
那么这里进行计算得时候则是取val数组中的字符得十进制进行计算得 也就是说第一次计算:h =310+49 最后得结果等于49 。要是有人不明白310 为啥等于零的话可以自行实践。
那么我们已经明白了是怎么计算得了 那么我给出整个计算得过程:
第一次 h =310+49 结果49,第二次 h =3149+50 结果1569,第三次 h =311569+51 结果48690
,第四次 h =31
48690+51 结果1509441。这就得到了hashCode得值了。

Integer HashCode

    public static void main(String[] args) throws Exception {Integer a = 1233;Integer b = 1233;System.out.println("a的哈希值: " + a.hashCode());System.out.println("b的哈希值: " + b.hashCode());System.out.println("a的内存地址标识: " + System.identityHashCode(a));System.out.println("b的内存地址标识: " + System.identityHashCode(b));
//todo 输出结果
//        a == b 的结果: false
//        a的哈希值: 1233
//        b的哈希值: 1233
//        a的内存地址标识: 1163157884
//        b的内存地址标识: 1956725890}

在这里插入图片描述

我们可以看出Integer得hashCode是直接把值返回出去当作hashCode

Long HashCode

    public static void main(String[] args) throws Exception {Long a = 1233L;Long b = 1233111111111111111L;System.out.println("a的哈希值: " + a.hashCode());System.out.println("b的哈希值: " + b.hashCode());System.out.println("a的内存地址标识: " + System.identityHashCode(a));System.out.println("b的内存地址标识: " + System.identityHashCode(b));
//todo 输出结果
//        a的哈希值: 1233
//        b的哈希值: -1555982910
//        a的内存地址标识: 1163157884
//        b的内存地址标识: 1956725890}

在这里插入图片描述

通过hashCode得源码我们可以看的出来 这里得是通过位运算进行计算得。
那么在深入理解得过程中我们要搞明白 这是怎么计算得,首先位运算在计算得过程中会把数字转换为二进制,那么long我们要知道是64位得 意思说转换二进制计算最多64位,其次它得存储上线是百兆也就是十九位。
这里得位运算数字转换为64位的大数字拆成 “前 32 位” 和 “后 32 位”,先把 “前 32 位” 挪到 “后 32 位” 的位置(左边补 0),再和原数字对比 “每一位”(相同为 0,不同为 1)这里有进行转换位int 那么就是取值右边32位转换成int 进行返回。
这里就不在过多得介绍其他得封装类型了。

类 hashCode

在这里插入图片描述
在这里插入图片描述

This is typically implemented by converting the internal

  • address of the object into an integer, but this implementation
  • technique is not required by the
  • Java™ programming language.

我们从这里的注释我们能看到 阿里说的面试题是对的,但是 要清楚 重写了就不对了 不重写要使用Object的
这里的注释说的是 这通常是通过将对象的内部地址转换为整数来实现的 但JavaTM 编程语言不需要这种实现技术。

在这里插入图片描述

这个我是从别人的博客上看到的 ,我能明确它看的是阿里的面试题 这个博客大概率是照着面试题抄的 只抄一半那就不对了 意思就变了 ,这一块说的太明确 太全面 就不对了 要明确说明 你是重写不重写的情况。重写了 用的是自己的hashCode进行计算的 自己没有重写才使用Object的。当我看到这个面试题的时候我很气愤,写的什么玩意面试题 。不如自己来写。

http://www.xdnf.cn/news/1457857.html

相关文章:

  • 多线程——线程状态
  • 并发编程——17 CPU缓存架构详解高性能内存队列Disruptor实战
  • ResNet(残差网络)-彻底改变深度神经网络的训练方式
  • linux——自定义协议
  • 多Agent协作案例:用AutoGen实现“写代码+测Bug”的自动开发流程
  • 秒店功能更新:多维度优化升级,助力商家经营
  • 当 LLM 遇上真实世界:MCP-Universe 如何撕开大模型 “工具能力” 的伪装?
  • 记录相机触发相关
  • 机器学习入门,第一个MCP示例
  • (D题|矿井突水水流漫延模型与逃生方案)2025年高教杯全国大学生数学建模国赛解题思路|完整代码论文集合
  • 生成式引擎优化(GEO):数字营销新标配,企业如何抢占AI搜索流量高地?
  • Trae + MCP : 一键生成专业封面的高阶玩法——自定义插件、微服务编排与性能调优
  • 设计模式六大原则2-里氏替换原则
  • Linux —— 环境变量
  • mysql中find_in_set()函数的使用, ancestors字段,树形查询
  • AI视频画质提升效果实用指南:提升清晰度的完整路径
  • [论文阅读] 软件工程 | REST API模糊测试的“标准化革命”——WFC与WFD如何破解行业三大痛点
  • 【论文阅读】-《Besting the Black-Box: Barrier Zones for Adversarial Example Defense》
  • AutoLayout与Masonry:简化iOS布局
  • (E题|AI 辅助智能体测)2025年高教杯全国大学生数学建模国赛解题思路|完整代码论文集合
  • 解密llama.cpp:Prompt Processing如何实现高效推理?
  • Nginx 实战系列(一)—— Web 核心概念、HTTP/HTTPS协议 与 Nginx 安装
  • Scikit-learn Python机器学习 - 特征预处理 - 归一化 (Normalization):MinMaxScaler
  • 孩子学手机里的坏毛病,怎样限制他打开某些APP?
  • Flutter 3.35.2 以上版本中 数字转字符串的方法指南
  • 机器学习基础-day05-深度学习框架PyTorch的tensor及PyTorch进行线性回归
  • 猫头虎AI 荐研|腾讯开源长篇叙事音频生成模型 AudioStory:统一模型,让 AI 会讲故事
  • 数据结构 之 【哈希的相关概念】
  • npm/pnpm软链接的优点和使用场景
  • 2025精选榜:4款好用的企业即时通讯软件推荐!安全有保障