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

深入解析HashMap的存储机制:扰动函数、哈希计算与索引定位

今天复习了一下HashMap的部分,写一篇博客记录一下今天学习内容

虽然之前学习过,但由于后来没怎么使用过而且也没复习基本忘得差不多了

在Java的HashMap中,高效存储键值对的核心在于哈希算法和索引定位。本文将结合源码逐步拆解存储流程,并给出代码示例。

 核心流程概述
  1. 扰动函数处理Key 通过扰动函数优化Key的哈希值,减少碰撞
  2. 存储哈希值 计算后的哈希值存入Node对象的hash字段
  3. 计算桶下标 用(n-1) & hash定位数组索引(n为数组长度)
  4. 存入数据结构 根据下标存入数组(或链表/红黑树)
 源码级分步解析(基于JDK 17)
① 扰动函数与哈希计算
static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

  • 作用:将原始哈希码的高16位与低16位异或
  • 目的:让高位参与运算,解决低位相同导致的哈希碰撞
  • 结果:扰动后的哈希值存储在Node的hash字段
② Node对象结构
static class Node<K,V> implements Map.Entry<K,V> {final int hash; // 存储扰动计算后的哈希值final K key;V value;Node<K,V> next; // 链表结构指针
}

③ 索引定位公式
// 在putVal方法中:
int index = (n - 1) & hash;
  • n-1:当前数组长度减1(如长度16→15,二进制0...01111
  • 位与运算:高效实现取模运算,hash % n等价于(n-1) & hash
    ④ 完整put流程伪代码
    final V putVal(int hash, K key, V value) {Node<K,V>[] tab; Node<K,V> p; int n, i;// 1. 首次使用触发初始化if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;// 2. 计算桶下标 (核心公式)i = (n - 1) & hash;// 3. 处理碰撞情况if ((p = tab[i]) == null) tab[i] = newNode(hash, key, value); // 无碰撞直接存else {// 碰撞后遍历链表/红黑树if (p.hash == hash && ...)// 更新已存在key的值else {// 链表新增节点if (binCount >= TREEIFY_THRESHOLD - 1)treeifyBin(tab, hash); // 链表转红黑树}}
    }

    举个🌰 实战示例与验证
    import java.util.*;public class HashMapDemo {public static void main(String[] args) {HashMap<String, Integer> map = new HashMap<>(16); // 初始容量16// 存储键值对 "A":1map.put("A", 1);// 手动验证存储过程String key = "A";// 1. 原始哈希码int hashCode = key.hashCode(); System.out.println("原始哈希码: 0x" + Integer.toHexString(hashCode));// 2. 扰动计算int perturbedHash = hash(key);System.out.println("扰动哈希值: 0x" + Integer.toHexString(perturbedHash));// 3. 计算桶下标 (n=16)int n = 16;int index = (n - 1) & perturbedHash;System.out.println("数组下标: " + index);}// JDK扰动函数实现static int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}
    }

    输出结果

    原始哈希码: 0x41
    扰动哈希值: 0x41  // 小值无高位变化
    数组下标: 1       // (16-1)=15: 0b1111 & 0x41=65 → 1
     设计原理深度剖析
    1. 为什么用位与代替取模?

      • 位运算(n-1) & hash%效率高10倍以上(实测)
      • 前提:数组长度必须是2的幂(保证n-1的二进制全为1)
    2. 扰动函数的必要性

       原始哈希: 0x1234abcd 和 0x5678abcdn=16时:未扰动 → 两值低位相同 → 碰撞扰动后 → 高位参与运算 → 低位不同 → 避免碰撞

         3.哈希碰撞策略

    最佳实践建议
    1. 避免自定义Key哈希碰撞
       // 错误实现:所有对象返回相同哈希码@Overridepublic int hashCode() { return 1; } // 导致HashMap退化为链表// 正确实现:组合关键字段哈希@Overridepublic int hashCode() {return Objects.hash(field1, field2);}

    1. 初始化容量优化
       // 预期存储1000个元素时new HashMap<>(2048); // 避免扩容 (1000/0.75≈1333 → 取2的幂2048)

    至此,本章的内容结束,后续我会补充一下在高并发情况下HashMap会出现的一些问题    

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

        相关文章:

      1. 信息收集4----(收集网站指纹信息)
      2. 20250821 圆方树总结
      3. 一、部署LNMP
      4. 实现自己的AI视频监控系统-第一章-视频拉流与解码3
      5. mac的m3芯使用git
      6. 18维度解密·架构魔方:一览无遗的平衡艺术
      7. LT8712SX,Type-C/DP1.4 /eDP转 DP1.4/HD-DVI2.0 带音频
      8. AXI GPIO S——ZYNQ学习笔记10
      9. Java项目:基于SpringBoot和VUE的在线拍卖系统(源码+数据库+文档)
      10. K 均值聚类(K-Means)演示,通过生成笑脸和爱心两种形状的模拟数据,展示了无监督学习中聚类算法的效果。以下是详细讲解:
      11. 【typenum】 19 类型相同检查(type_operators.rs片段)
      12. JavaWeb前端03(Ajax概念及在前端开发时应用)
      13. SD 节点学习
      14. ZStack Zaku替代VMware Tanzu:六项对比、构建虚拟机+容器一体化架构
      15. HTTP 403 错误:后端权限校验机制深度解析
      16. Matplotlib数据可视化实战:Matplotlib高级使用技巧与性能优化
      17. 用OpencvSharp编写视频录制工具
      18. Matplotlib数据可视化实战:Matplotlib数据可视化入门与实践
      19. 【Android】悬浮窗清理
      20. Pytorch基础学习--张量(生成,索引,变形)
      21. 从系统漏洞归零到候诊缩短20%:一个信创样本的效能革命
      22. 机器学习聚类与集成算法全解析:从 K-Means 到随机森林的实战指南
      23. CRMEB私域电商系统后台开发实战:小程序配置全流程解析
      24. 贪吃蛇游戏(纯HTML)
      25. 什么是区块链?从比特币到Web3的演进
      26. 图像中物体计数:基于YOLOv5的目标检测与分割技术
      27. 十分钟速通堆叠
      28. 智慧城市SaaS平台/市政设施运行监测系统之空气质量监测系统、VOC气体监测系统、污水水质监测系统及环卫车辆定位调度系统架构内容
      29. 终结开发混乱,用 Amazon Q 打造AI助手
      30. 华为云ModelArts+Dify AI:双剑合璧使能AI应用敏捷开发