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

关于HashMap中的二次Hash

问题引入 : 

在学习HashMap的底层源码的时候 ,发现 : 

在putVal的时候会调用一个hash()对key进行hash操作 : 

 

先通过key.hashCode()获得了哈希值h ,这是第一次hash操作  ;

再进行 : 

h^h>>>16

 也就是二次Hash操作 ;

那么为什么要进行二次hash操作呢 ? 

二次hash的效果代码演示 : 

package com.it.Map;import java.util.* ;public class DistributionAffectedByCapacity {public static int[] randomArray(int n) {Random random = new Random();int[] arr = new int[n];for (int i = 0; i < n; i++)arr[i] = random.nextInt(100000000) ;return arr;}// 要么取余16==0 , 1// 构造一千个随机数 ,每个随机数要求取余16==0或者==1public static int[] lowSameArray(int n) {Random random = new Random();int[] arr = new int[n];for (int i = 0; i < n; i++){int x = random.nextInt(100000000) ;if(x%16==0||x%16==2) arr[i] = x;else {if (x % 16 >= 8) x -= (x % 16);else x = x - (x % 16) + 2;arr[i] = x ;}}return arr;}public static void printDistribution(int[] arr, int[] sizes) {for (int size : sizes) {Map<Integer, Integer> map = new HashMap<>();for (int i : arr) {// int p = i ;int p = i^i>>>16 ;// 增加随机性// 假设不进行二次hash ,原hash的高位根本不会影响得到的下标,在size比较小的情况之下 ,只会受到低位的影响 ;// HashMap通过将哈希码的高16位与低16位进行异或运算,得到一个新的哈希码,这样就可以让高位也参与到运算,这个函数也被称作「扰动函数」。map.put(p%size, map.getOrDefault(p%size, 0) + 1);}for(int i=0;i<size;i++){if(map.get(i)==null) System.out.print(i+" : "+ 0 + " ,");else System.out.print(i+" : "+ map.get(i) + " ,");}}}public static void main(String[] args) {int[] a = randomArray(1000);// 足够随机int[] b = lowSameArray(1000);// System.out.println(Arrays.toString(a));System.out.println(Arrays.toString(b));int[] sizes = {16} ;printDistribution(b, sizes);}
}

1 . 先用随机数生成一个size=1000的数组 , 模拟放入大小为16的hash数组中 : 

能看到数据还是比较分散的 ;

2 . 先用随机数生成一个size=1000的数组(但是处理是数组中只存在模16==0/1的数据) , 模拟放入大小为16的hash数组中 : 

可以发现数据及其分布不均 ;

3 . 先用随机数生成一个size=1000的数组(但是处理是数组中只存在模16==0/1的数据) , 模拟放入大小为16的hash数组中  , 这次模拟进行二次hash操作 : 

可以看到数据较上次是随机的

结论 : 

  • 假设不进行二次hash ,原hash的高位根本不会影响得到的下标,在size比较小的情况之下 ,只会受到低位的影响 , 就算散列值分布得再松散 ,只取低位的几位的情况下(假设4位) ,很可能出现重复 ,发生hash碰撞的概率也会增大 ;

  • HashMap通过将哈希码的高16位与低16位进行异或运算,得到一个新的哈希码,这样就可以让高位也参与到运算,这个函数也被称作「扰动函数」。

相关文章:

  • rtsp 协议推流接收(tcp udp)
  • 详解调用钉钉AI助理消息API发送钉钉消息卡片给指定单聊用户
  • Layui表单查询导出
  • IDEA激活失败--脚本分析
  • 实习结帖(flask加上AIGC实现设计符合OpenAPI要求的OpenAPI Schema,让AIGC运行时可以调用api,协助公司门后迁移新后端等)
  • 以太网交换安全:MAC地址表安全
  • 51单片机学习第六课---B站UP主江协科技
  • 读数据湖仓04数据架构与数据工程
  • SkyWalking 自定义链路追踪
  • 【ShuQiHere】从机器语言到汇编语言:深入理解 LC-3 编程 ️
  • 矩阵学习过程中的一些思考
  • 万界星空科技铜拉丝行业MES系统,实现智能化转型
  • 7天的Django实战学习计划
  • ECharts 快速使用
  • mybatisplus的查询,分页查询,自定义多表查询,修改的几种写法
  • android高仿小视频、应用锁、3种存储库、QQ小红点动画、仿支付宝图表等源码...
  • CSS相对定位
  • ES6核心特性
  • Javascript 原型链
  • Java的Interrupt与线程中断
  • JS 面试题总结
  • scrapy学习之路4(itemloder的使用)
  • Twitter赢在开放,三年创造奇迹
  • vue 配置sass、scss全局变量
  • 将回调地狱按在地上摩擦的Promise
  • 开发基于以太坊智能合约的DApp
  • Hibernate主键生成策略及选择
  • ###C语言程序设计-----C语言学习(3)#
  • #1014 : Trie树
  • #define 用法
  • (2)从源码角度聊聊Jetpack Navigator的工作流程
  • (26)4.7 字符函数和字符串函数
  • (Matlab)使用竞争神经网络实现数据聚类
  • (WSI分类)WSI分类文献小综述 2024
  • (佳作)两轮平衡小车(原理图、PCB、程序源码、BOM等)
  • (算法)求1到1亿间的质数或素数
  • (原)本想说脏话,奈何已放下
  • .gitignore文件使用
  • .libPaths()设置包加载目录
  • .NET Core、DNX、DNU、DNVM、MVC6学习资料
  • .net mvc部分视图
  • .NET Reactor简单使用教程
  • .Net 路由处理厉害了
  • .NET 使用 XPath 来读写 XML 文件
  • .NET/C# 检测电脑上安装的 .NET Framework 的版本
  • .netcore 如何获取系统中所有session_ASP.NET Core如何解决分布式Session一致性问题
  • .NET导入Excel数据
  • .NET之C#编程:懒汉模式的终结,单例模式的正确打开方式
  • .Net转Java自学之路—基础巩固篇十三(集合)
  • .Net组件程序设计之线程、并发管理(一)
  • .ui文件相关
  • /dev下添加设备节点的方法步骤(通过device_create)
  • /etc/motd and /etc/issue
  • @KafkaListener注解详解(一)| 常用参数详解
  • [ C++ ] STL_vector -- 迭代器失效问题