HashMap原理深入理解

2023-05-16

hashing(哈希法)的概念

散列法(Hashing)是一种将字符组成的字符串转换为固定长度(一般是更短长度)的数值或索引值的方法,称为散列法,也叫哈希法。由于通过更短的哈希值比用原始值进行数据库搜索更快,这种方法一般用来在数据库中建立索引并进行搜索,同时还用在各种解密算法中。

对比:Hashtable、HashMap、TreeMap

Hashtable 是早期Java类库提供的一个哈希表实现,本身是同步的,不支持 null 键和值,由于同步导致的性能开销,所以已经很少被推荐使用。

HashMap与 HashTable主要区别在于 HashMap 不是同步的,支持 null 键和值等。通常情况下,HashMap 进行 put 或者 get 操作,可以达到常数时间的性能,所以它是绝大部分利用键值对存取场景的首选。

TreeMap 则是基于红黑树的一种提供顺序访问的 Map,和 HashMap 不同,它的 get、put、remove 之类操作都是 O(log(n))的时间复杂度,具体顺序可以由指定的 Comparator 来决定,或者根据键的自然顺序来判断。

HashMap概念和底层结构

HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。HashMap储存的是键值对,HashMap很快。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

HashMap 内部结构:可以看作是数组和链表结合组成的复合结构,数组被分为一个个桶(bucket),每个桶存储有一个或多个Entry对象,每个Entry对象包含三部分key(键)、value(值),next(指向下一个Entry),通过哈希值决定了Entry对象在这个数组的寻址;哈希值相同的Entry对象(键值对),则以链表形式存储。如果链表大小超过树形转换的阈值(TREEIFY_THRESHOLD= 8),链表就会被改造为树形结构。

hashMap的结构示意图如下:
这里写图片描述
查询时间复杂度:HashMap的本质可以认为是一个数组,数组的每个索引被称为桶,每个桶里放着一个单链表,一个节点连着一个节点。很明显通过下标来检索数组元素时间复杂度为O(1),而且遍历链表的时间复杂度是O(n),所以在链表长度尽可能短的前提下,HashMap的查询复杂度接近O(1)

数组:存储区间连续,占用内存严重,寻址容易,插入删除困难;
链表:存储区间离散,占用内存比较宽松,寻址困难,插入删除容易;
Hashmap综合应用了这两种数据结构,实现了寻址容易,插入删除也容易。

HashMap的工作原理

HashMap的工作原理 :HashMap是基于散列法(又称哈希法)的原理,使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket(桶)位置来储存Entry对象。HashMap是在bucket中储存键对象和值对象,作为Map.Entry。并不是仅仅只在bucket中存储值。

HashMap具体的存取过程

put存值的方法,过程如下:
这里写图片描述
①.判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容;

②.根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向⑥,如果table[i]不为空,转向③;

③.判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向④,这里的相同指的是hashCode以及equals;

④.判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对,否则转向⑤;

⑤.遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可;

⑥.插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold,如果超过,进行扩容。

get取值的方法,过程如下:

①.指定key 通过hash函数得到key的hash值
int hash=key.hashCode();

②.调用内部方法 getNode(),得到桶号(一般为hash值对桶数求模)
int index =hash%Entry[].length;
jdk1.6版本后使用位运算替代模运算,int index=hash&( Entry[].length - 1);

③.比较桶的内部元素是否与key相等,若都不相等,则没有找到。相等,则取出相等记录的value。

④.如果得到 key 所在的桶的头结点恰好是红黑树节点,就调用红黑树节点的 getTreeNode() 方法,否则就遍历链表节点。getTreeNode 方法使通过调用树形节点的 find()方法进行查找。由于之前添加时已经保证这个树是有序的,因此查找时基本就是折半查找,效率很高。

⑤.如果对比节点的哈希值和要查找的哈希值相等,就会判断 key 是否相等,相等就直接返回;不相等就从子树中递归查找。

如何重新调整HashMap的大小

“如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?”
HashMap的扩容阈值(threshold = capacity* loadFactor 容量范围是16~2的30次方),就是通过它和size进行比较来判断是否需要扩容。默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,将会创建原来HashMap大小的两倍的bucket数组(jdk1.6,但不超过最大容量),来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置。

解决 hash 冲突的常见方法

针对哈希表直接定址可能存在hash冲突,举一个简单的例子,例如:
第一个键值对A进来,通过计算其key的hash得到的index=0。记做:Entry[0] = A。
第二个键值对B,通过计算其index也等于0, HashMap会将B.next =A,Entry[0] =B,
第三个键值对C,通过计算其index也等于0,那么C.next = B,Entry[0] = C;
这样我们发现index=0的地方事实上存取了A,B,C三个键值对,它们通过next这个属性链接在一起。 对于不同的元素,可能计算出了相同的函数值,这样就产生了hash 冲突,那要解决冲突,又有哪些方法呢?具体如下:

a. 链地址法:将哈希表的每个单元作为链表的头结点,所有哈希地址为 i 的元素构成一个同义词链表。即发生冲突时就把该关键字链在以该单元为头结点的链表的尾部。

b. 开放定址法:即发生冲突时,去寻找下一个空的哈希地址。只要哈希表足够大,总能找到空的哈希地址。

c. 再哈希法:即发生冲突时,由其他的函数再计算一次哈希值。

d. 建立公共溢出区:将哈希表分为基本表和溢出表,发生冲突时,将冲突的元素放入溢出表。

HashMap采用哪种方法解决冲突的呢?

HashMap 就是使用链地址法来解决冲突的(jdk8中采用平衡树来替代链表存储冲突的元素,但hash() 方法原理相同)。当两个对象的hashcode相同时,它们的bucket位置相同,碰撞就会发生。此时,可以将 put 进来的 K- V 对象插入到链表的尾部。对于储存在同一个bucket位置的链表对象,可通过键对象的equals()方法用来找到键值对。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

HashMap原理深入理解 的相关文章

  • 编译android7.1源码环境的配置以及中途可能出现问题的总结

    在项目要求将apk文件打包到安卓系统7 1出了一大堆问题 xff0c 由于我是windows10系统 xff0c 所以在电脑上装了个virtualbox虚拟机 xff0c 并且装上了ubuntu 18 04 2 接下来就是在这个虚拟机系统上
  • Ubuntu 创建开机自启动脚本

    由于在ubuntu上面跑了很多服务 xff0c 每次开机都需要自己手动去启动服务太麻烦 下面记录一下开机自启动脚本的编写 xff0c 以便往后查阅 1 适用系统Ubuntu18 ubuntu20 2 切换到etc目录 cd etc 3 创建
  • 关于合法的出栈顺序

    可以按照下面的方法来判断的 xff1a 假如入栈顺序为1234 xff0c 给定一个出栈序列 xff0c 如2431 xff0c 它是合法的 因为对于出栈序列中的每一个数字 xff0c 在它后面的 比它小的所有数字 xff0c 一定是按递减
  • 增量测试:自顶向下测试&自底向上测试

    本博客主要内容 xff1a 自顶向下测试和自底向上测试的优缺点 xff1b 软件开发周期流程 xff1b 不同的测试方法针对不同的测试阶段 一 自顶向下测试 xff1a 优点 xff1a 1 如果主要的缺陷发生在程序的顶层将非常有利 2 一
  • 局域网 Ubuntu 16.04.4 安装 Docker 18.06.0-ce 笔记

    局域网内搭建Ubuntu环境下的Docker Engine 17 06 0 43 Docker Compose 1 14 0 43 的环境运行项目 网上查找到很多方法 xff0c 但是安装总有报错 解决报错办法 xff1a 一 下载了doc
  • oracle监听器日志过大-处理办法

    原则是lsnrctl status xff0c 找到日志文件位置 xff0c 就是给你看的那些信息中的log file xff0c 删除即可 但是监听器在运行状态下 xff0c 无法删除 办法1 xff1a 将监听器stop xff0c 然
  • 网络——IPV4地址(二)

    划分子网的IPv4地址 在IP地址中增加一个 子网号字段 xff0c 使得两级IP地址变成三级IP地址 这种做法称为子网划分 三级IP地址结构如下 xff1a IP地址 61 lt 网络号 gt lt 子网号 gt lt 主机号 gt 子网
  • 创建一个基于WebPacket的TypeScript项目【一】

    创建一个基于WebPacket的TypeScript项目 安装node js环境建立目录结构在 96 templates 96 目录新建 96 template index html 96 并写入 安装VSCode创建一个NPM项目确认 安
  • 记一次grpc arm-hisiv400-linux交叉编译

    时间紧 xff0c 先大概说明一下 xff0c 有时间了再补充详细的说明 grpc 交叉编译 需要先编译出pc版的protobuff 和 grpc xff0c 安装到指定的路径 xff0c 在做交叉编译时需要protoc 和grpc cpp
  • SDN协议与SD-WAN中使用的协议相比有何差别?

    通过建立在物理网络上方抽象的虚拟网络 xff0c 软件定义的网络可实现更灵活的网络管理和操作 代替在硬件级别编程的物理网络设备来驱动网络控制 xff0c 软件定义网络 SDN 引入了一个软件驱动的控制器来处理这些任务并使变更能够实时进行 x
  • SDN和SD-WAN的概念别再搞混了—Vecloud微云

    最近 xff0c SD WAN在融资领域是一个比较热的话题 国外几家SD WAN的头部企业不断地获得融资 xff0c 也包括被思科 VMware等巨头收购和兼并 xff0c 国内创业公司推出了各种SD WAN产品和解决方案 不得不说 xff
  • SDN和SD-WAN有本质区别—Vecloud微云

    作为软件定义网络 xff08 SDN xff09 技术中的一个细分 xff0c 软件定义广域网 xff08 SD WAN xff09 无疑是从2015年到现在企业级广域网布局中最热门的技术之一 SDN SDN旨在支持局域网 xff08 LA
  • 企业MPLS专线的价格及计费方式——微云专线

    企业MPLS专线的费用 MPLS最大技术特色为可以指定资料包传送的先后顺序 xff0c 提供优质增值服务 xff0c 如 xff1a 差别服务 Diff serv 服务级别 CoS 和服务质量 QoS 等 因为MPLS整合了第三层路由和第二
  • 网络专线MPLS配置原理

    MPLS最早的意思是让中间设备只是查找一个表 xff0c 这样就可以相对更快地工作 xff0c 随着CPU运算能力的不断提高 xff0c 包的交换方式从原始到传统 一次路由多次交换 xff0c 最后再到快速的CEF数据交换方式 xff0c
  • 什么是BGP,BGP的优点有哪些?-Vecloud

    什么是BGP 边界网关协议 BGP 是运行于 TCP 上的一种自治系统 AS 的路由协议 xff0c 是唯一能够妥善处理不相关路由域间的多路连接的协议 通俗点讲 中国电信 中国联通 中国移动和一些拥有AS自治域的大型民营IDC运营商就可以通
  • 从linux接入到windows远程桌面

    Windows 提供了一种远程桌面系统 xff0c 可使用户远程登录进行系统管理或作为终端服务器运行各种应用软件 要连接Windows远程桌面 xff0c 需在Windows客户端安装 相应的软件 xff08 tsclient xff09
  • ssh命令使用总结

    前言 为了安全起见 xff0c 公司服务器只给开放了一个ssh端口其他服务器通过跳板机登录 xff0c 这样难免会造成一些不便 xff0c 但是ssh总体还比较强大 xff0c 可以通过配置 xff0c 方便容易的进行登录 也可以通过端口转
  • **matlab subs函数**

    matlab subs函数 matlab中subs 是符号计算函数 xff0c 表示将符号表达式中的某些符号变量替换为指定的新的变量 xff0c 常用调用方式为 xff1a subs S OLD NEW 表示将符号表达式S中的符号变量OLD
  • Python爬虫库推荐,建议收藏留用

    很多人学Python xff0c 都是从爬虫开始的 xff0c 毕竟网上类似的资源很丰富 xff0c 开源项目也非常多 Python学习网络爬虫主要分3个大的版块 xff1a 抓取 xff0c 分析 xff0c 存储 当我们在浏览器中输入一
  • 【深度学习】:Faster RCNN论文详解

    Faster RCNN详解 Faster RCNN 是在Fast RCNN的基础上 xff0c 进一步改进 xff0c 解决select search 算法选择候选框速度太慢的问题 Faster R CNN Towards Real Tim

随机推荐

  • 优化命令之taskset——查询或设置进程绑定CPU

    目录 一 xff1a taskset概述 二 xff1a 安装taskset工具 2 1taskset语法 2 2taskset用法 2 2 1指定PID为8528的进程在CPU1上运行 2 2 2更改具体某一进程 xff08 或 线程 x
  • Kubernetes:(十八)flannel网络

    目录 一 xff1a 什么是Flannel 1 1 Flannel实现原理 1 2 数据转发流程 二 xff1a Flannel网络概述 2 1 Vxlan 模式 2 1 1 通信流程 2 1 2 部署 2 1 3 相关配置 2 1 4 卸
  • 串口调试工具 O-ComTool V1.1.3

    新版本 O ComTool V2 0 0点击访问 写在之前 由于本人从事嵌入式工作 xff08 物联网方向 xff09 xff0c 经常需要和串口打交道 xff0c 面对各种规约 协议 xff0c 调试实在麻烦 xff0c 于是本人根据同事
  • 关于VSCODE的插件 一

    官方API文档 1 要学好TypeScript 官方教程 1 1TypeScript是一门弱类型语言 强类型和弱类型主要是站在变量类型处理的角度进行分类的 这些概念未经过严格定义 xff0c 它们并不是属于语言本身固有的属性 xff0c 而
  • Keil_debug

    提示 xff1a 文章写完后 xff0c 目录可以自动生成 xff0c 如何生成可参考右边的帮助文档 目录 前言 一 使用步骤 1 引入库 2 读入数据 总结 前言 程序员的工作中调试 debug xff0c 修bug xff0c 改bug
  • 前端基础知识梳理——html中的长度单位与颜色RGB值

    前言 我们在编写前端业务的时候很定会使用到长度单位 xff0c 这对于我们构建前端元素 xff0c 布局 xff0c 定位是很重要的 就像我们在盖房子的时候 xff0c 需要使用标尺线精确的测量 xff0c 也要使用颜色用于装饰页面 在ht
  • 【Hadoop】熟悉常用的HBase操作(Java实现)

    一 实验平台 操作系统 xff1a Linux deepin Hadoop版本 xff1a 2 7 7 HBase版本 xff1a 1 2 6 Java IDE xff1a Eclipse 二 实验内容 1 使用 Hadoop提供的Java
  • Hadoop权威指南:知识梳理(一)

    第一章 xff1a 初识Hadoop MapReduce三大设计目标 xff1a 为只需要短短几分钟或几个小时就可以完成的作业提供服务运行于同一个内部有高速网络连接的数据中心内数据中心内的计算器都是可靠的 专门的硬件 提供Hadoop支持的
  • tasksel —– ubuntu里面方便安装服务的软件

    用这个软件可以方便安装dns server lamp kubuntu desktop ubuntu desktop xubuntu之类的软件包 这个软件在ubuntu server里是预装的 xff0c 而在桌面版里是不预装的 xff0c
  • 信号量能被 FixedThreadPool 替代吗?

    Semaphore 信号量 从图中可以看出 xff0c 信号量的一个最主要的作用就是 xff0c 来控制那些需要限制并发访问量的资源 具体来讲 xff0c 信号量会维护 许可证 的计数 xff0c 而线程去访问共享资源前 xff0c 必须先
  • 带参数的宏定义(宏函数)

    宏函数没有普通函数压栈 跳转 返回等的开销 xff0c 可以提高程序的效率 宏的名字中不能有空格 xff1b 用括号括住每一个参数 xff0c 并括住宏的整体定义 xff1b 用大写字母表示宏的函数名 define SUM xff08 a
  • OVN&OVS代码下载、编译安装以及运行步骤

    1 代码下载 新建代码目录 home code 下载ovs代码 xff1a git clone b branch 2 15 https github com openvswitch ovs git 下载ovn代码 xff1a git clo
  • CMMI过程改进反例

    xfeff xfeff 最近一直在看 CMMI 的资料 xff0c 越看觉得越有意思 xff0c 今天看到过程改进的时候 xff0c 突然想起来之前所在的公司发生的过程改进相关的事儿来 公司通过 CMMI3 级认证之后 xff0c PMO
  • hadoop 超详细入门wordcount

    概述 今天博客收到了第一条评论 xff0c 感觉很赞哦 xff0c 最近一直在学习hadoop xff0c 主要是结合 实战Hadop xff1a 开启通向云计算的捷径 刘鹏 xff0c 然后apache官网的doc xff08 还是要以官
  • NOIP2018集训总结

    由于语文水平有限 xff0c 精美的桥段 xff0c 跌宕起伏的情节是不可能的了 xff0c 也许看起来会很智障 初赛 前排沙发祝贺墙根火 这次初赛主要在 读程序填空 上失分较多 先找几个不是原因的原因 xff1a 考前那晚宿舍里人巨吵考前
  • Hadoop大数据入门到实战(第四节) - HDFS文件系统(使用)

    这一小节我们来学习 xff1a 1 HDFS的设计 xff0c 2 HDFS常用命令 HDFS的设计 分布式文件系统 客户 xff1a 帮我保存一下这几天的数据 程序猿 xff1a 好嘞 xff0c 有多大呢 xff1f 客户 xff1a
  • HashMap什么时候重写hashcode和equals方法,为什么需要重写

    转载自 xff1a http bdcwl blog 163 com blog static 765222652009112744733937 HashSet内部是通过HashMap实现 只有使用排序的时候才使用TreeMap 否知使用Has
  • 运维如何解决终端部门投诉

    东部某省会城市的联通分公司 xff0c 内部业务系统都运行在VMware为基础的虚拟化环境中 xff0c 但联通的网络运维部在运维时却遇到了很多难题 由于V center的operation manager等云管产品只能监控到虚拟化网络的基
  • Latex并排显示两张图片

    begin figure htbp begin minipage t 0 5 linewidth centering includegraphics width 61 textwidth figures karate graph pdf c
  • HashMap原理深入理解

    hashing 哈希法 的概念 散列法 xff08 Hashing xff09 是一种将字符组成的字符串转换为固定长度 xff08 一般是更短长度 xff09 的数值或索引值的方法 xff0c 称为散列法 xff0c 也叫哈希法 由于通过更