hashmap原理_HashMap原理jdk7和jdk8的区别

2023-11-10

一、hashMap的jdk1.7和jdk1.8区别

1、实现方式:

jdk7中使用数组+链表来实现,jdk8使用的数组+链表+红黑树

2、新节点插入到链表是的插入顺序不同

jdk7插入在头部,jdk8插入在尾部
jdk7中,如何在头部插入?看addEntry的createEntry方法:

d8dcaf564ff21ec50b24316c398fb1d6.png

其实很简单的道理,就是创建一个new Entry,这个newEntry的next是e(参照new Entry<>(hash,key,value,e)的实现),这个e就是之前的头结点,然后把当前table的位置放上新建的Entry,也就是插入在链表的头部了。为什么jdk7要插入在头部呢?因为插入头部效率高,不需要遍历整个链表。

jdk8中,代码放在尾部,代码实现是putVal的一段代码:

5f24e7a530e031788d72ca6cdba48b4a.png

可以很容易看出是放在尾部。


jdk8中为什么新来的元素是放在节点的后面?我们知道jdk8链表大于8的时候就要树化,本身就要算这个链表的长度有多大,链表算大小就要一个个去遍历了,遍历到了才能知道数量,也可以直接把数据放到后面了,这样也是很方便,减少了把数据移动到头数组位置这一步,效率也提高了。而且,jdk7中出现的那个HashMap死循环bug不会有了,因为这里是只是平移,没有调换顺序。

3、jdk8的hash算法有所简化

jdk7默认初始化大小16,加载因子0.75。如果传入了size,会变为大于等于当前值的2的n次方的最小的数。

b6a1acd233d0047724d89934e9b49bf9.png

为什么是2次方数?因为indexFor方法的时候,h&(length-1),length是2的次方,那么length-1总是00011111等后面都是1的数,h&它之后,其实就相当于取余,与的效率比取余高,所以用了这种方式达到高效率。

下面是jdk7的indexfor的实现:

080e8344ac5a9d65842bad5df92e6d3b.png

另外在hash函数里面方法里面,数会经过右移,为什么要右移?因为取余操作都是操作低位,hash碰撞的概率会提高,为了减少hash碰撞,右移就可以将高位也参与运算,减少了hash碰撞。

哈希函数如下:

984d3769aaaa6bb719f2776bb7c32506.png

所以总体来说jdk7的hash算法复杂jdk8的右移比较简单,没有jdk7那么复杂。jdk8的哈希函数如下:

d3a4a9eae2160a030727b0b25b090e84.png

为什么jdk8的hash函数会变简单?jdk8中我们知道用的是链表过度到红黑树,效率会提高,所以jdk8提高查询效率的地方由红黑树去实现,没必要像jdk那样右移那么复杂。

扩容机制有所优化

在jdk8中,实际上也是通过取余找到元素所在的数组的位置的,jdk8里面取余的方式在putVal里面:

40f7c7788f6fe34e8af57773af8594d3.png

上面那段代码就是。

我们假设,在扩容之前,key取余之后留下了n位。扩容之后,容量变为2倍,所以key取余得到的就有n+1位。在这n+1位里面,如果第1位是0,那么扩容前后这个key的位置还是在相同的位置(因为hash相同,并且余数的第1位是0,和之前n位的时候一样,所以余数还是一样,位置就一样了);如果这n+1位的第一位是1,那么就和之前的不同,那么这个key就应该放在之前的位置再加上之前整个数组的长度的位置,也就是这段代码:

81cc95b47a5253338e488d66cd1f1d63.png

这样子就减少了移动所有数据带来的消耗,jdk为了优化,真是“费尽心思”。

其他区别

节点表示

在jdk7中,节点是Entry,在jdk8中节点是Node,其实是一样的。

关于jdk7HashMap的一些细节

1、jdk7里面addEntry方法扩容的条件size>threshold,还有一个很容易忽略的,就是null!=table[bucketIndex],这个是什么意思?意思是如果当前放进来的值的那个位置也被占用了,才进行扩容,否则还是放到那个空的位置就行了,反正不存在hash冲突。(但是在jdk8里面取消了这个限制,只要达到了threshold,不管原来的位置空不空,还是要扩容)

2、jdk7resize方法多线程死循环的bug:http://www.importnew.com/22011.html 在jdk8的这个bug已经解决了。

关于jdk8HashMap的一些细节

1、jdk8 默认初始化大小16,加载因子0.75。还有一个默认的树化的大小8。非树化大小为6,也就是红黑树的元素小于6的时候,又会变回一个链表。为什么是8和6?


参考:
HashMap在JDK1.8及以后的版本中引入了红黑树结构,若桶中链表元素个数大于等于8时,链表转换成树结构;若桶中链表元素个数小于等于6时,树结构还原成链表。因为红黑树的平均查找长度是log(n),长度为8的时候,平均查找长度为3,如果继续使用链表,平均查找长度为8/2=4,这才有转换为树的必要。链表长度如果是小于等于6,6/2=3,虽然速度也很快的,但是转化为树结构和生成树的时间并不会太短。


还有选择6和8,中间有个差值7可以有效防止链表和树频繁转换。假设一下,如果设计成链表个数超过8则链表转换成树结构,链表个数小于8则树结构转换成链表,如果一个HashMap不停的插入、删除元素,链表个数在8左右徘徊,就会频繁的发生树转链表、链表转树,效率会很低。


2、jdk8的putVal方法:i = (n - 1) & hash这个其实也就是取余操作了。

关于HashMap的一些其他细节

1、为什么重写对象equals方法的时候,要重写hashcode,跟hashmap有关系吗?
java object类默认有一个hashcode方法,返回的是对象的地址。
hashmap是根据hashcode返回value的,如果没有重写hashcode,即使重写了equals方法之后,两个对象equals之后相同,但是hash之后却不同,在使用hashMap的时候通过一个对象作为key去获取另外一个对象为key存进去的value的时候,很有可能返回为空,这就是原因。所以和hashmap有关系,不如说和hashcode有关系。

2、ConcurrentModificationException为什么会有这个错误?
首先我们看一段代码:

b11c5383c38e51361eff7128b2a4f1ce.png

上面这个会报错下面这个不会:

99807680beb3762c3e02234b654f4ca9.png

为什么呢?我们把上面的代码转换一下,我们把foreach变为下面这个也是一样的原理:

1c94eb78e31da6972831b49b51b6012e.png

看源码expectedModCount的位置,HashIterator.nextNode的位置会出现这个错误,看看源码。

28fd589ba8c88a12a9cb8d253741bf0a.png

在上面的例子中,到了一定的时间,expectedModCount是2,但是modCount变成3,就会报错。
在多线程情况下,一个线程在遍历,一个线程在remove就会出现这种情况。
其实也不一定在最后面添加的判断就不会,具体看hash实现情况。

3、设计hashmap需要解决的问题

1)找到hash函数

2)解决冲突

6ff4fc5048cfc2790b31a589ac0a9ba7.png

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

hashmap原理_HashMap原理jdk7和jdk8的区别 的相关文章

  • sqli--labs 进阶篇 23_24关

    第二十三关 基于单引号的过滤字符注入 直接爆点 测试是否报错 从下面的提示信息 可以知道是单引号 id 代码审计 进一步确定自己的推论 源码中 存在过滤掉一些注释语句 id 1 推测结构 参数XX limit 0 1 利用回显确定下自己的推
  • 【Linux 速查手册】基于CentOS的Linux 文件结构以及在搭建LAMP环境

    文章目录 LAMP Linux 主要文件结构 Apache 作为 Web 服务器的文件结构 在Centos 中 home目录和 目录的区别 写在最后 LAMP LAMP是指使用 Linux Apache MySql PHP 搭建而成的网站
  • 2018最有前景的编程语言, 你选对了吗?

    对于程序员来说 世间最可怕的事情 莫过于 刚刚学过的编程语言就已经过时 对于求职者来说 了解受欢迎的编程语言及趋势 无论是对找工作 还是规划将来的职业发展 都有很大的好处 基于各种可信来源的数据统计 我对2018年初IT行业编程语言的状态
  • Debian 10驱动Broadcom 无线网卡

    用lspci命令查询无线网卡品牌 运行下面代码后 重启即可 apt get install linux image uname r sed s linux headers uname r sed s broadcom sta dkms
  • QT处理日志文件

    由于实际生产需要 软件系统的运行 会产生大量的日志文件 有时候一天就能产生超过百万条log记录 那么为了能够处理日志文件 查询并且找到我们想要的报错信息 因此不得不考虑怎么实现 打开大日志文件的可行方法 在这里我采用的是内存映射的方式去读取
  • 深入理解神经网络:使用Python实现简单的前馈神经网络

    在本文中 我们将深入理解前馈神经网络的原理 并使用Python编程实现一个简单的前馈神经网络 我们将使用NumPy库来处理矩阵运算 并将逐步解释神经网络的各个组成部分 包括神经元 激活函数 前向传播 反向传播和梯度下降 最后 我们将以一个简
  • k8s-基础入门

    目录 一 k8s的特性 二 kubernetes的基本组件 1 Pod 最小的资源单位 1 1 Pod的两个分类 2 资源清单 3 Pod 控制器 维护Pod状态 期望值 4 服务发现 Service同一个访问入口 5 存储服务分类 6 调
  • linux rpm软件包管理,linux之rpm软件包管理

    1 RPM包的命名规则 例如 httpd 2 2 15 15 el6 centos 1 i686 rpm httpd 软件包名 2 2 15 软件版本 15 发行次数 e16 centos 适合的linux平台 i686 适合的硬件平台 r
  • Angular之ngModel报错:angular-can‘t-bind-to-‘ngModel‘---

    做双向绑定时 如果遇见Angular Can t bind to ngModel since it isn t a known property of input 问题 这是由于没有在当前组件所属的Module中引用FormModule 注
  • 操作系统-进程概念与进程控制块

    进程 在学习操作系统时 对于进程我们经常能看到如下几个定义 一个正在执行的程序 一个正在计算机上执行的程序实例 能分配给处理器并由处理器执行的实体 由一组执行的指令 一个当前状态和一组相关的系统资源表征的活动单元 以上定义都是很抽象的 将进
  • Papers with Code一个查找论文和对应代码的神器

    0x01 Papers with Code是什么 Papers with Code 是一个包含机器学习论文及其代码实现的网站 大多数论文都是有GitHub代码的 这个网站很牛逼的地方就是对机器学习方向做了任务分类 检索对应的论文 数据 代码
  • Shell 编程:探索 Shell 的基本概念与用法

    目录 Shell 简介 Shell 脚本 Shell 脚本运行 Shell 变量 1 创建变量和赋值 2 引用变量 3 修改变量的值 4 只读变量 5 删除变量 6 环境变量 Shell 字符串操作 1 拼接字符串 2 字符串长度 3 字符
  • 实现antd中Form、Form.Item组件

    实现antd中Form Form Item组件 初始化项目 使用create react app初始化项目后创建MyRcFieldForm文件 代码如下 import React Component useEffect from react
  • SWOT分析模型

    SWOT分析模型 出自 MBA智库百科 http wiki mbalib com SWOT分析模型 SWOT Analysis SWOT分析法 也称TOWS分析法 道斯矩阵 即态势分析法 20世纪80年代初由美国旧金山大学的管理学教授韦里克
  • Flask+Nginx+Gunicorn部署应用实现

    实际的开发中 不能使用flask搭建的轻型服务器 无法满足性能要求 在生产环境中可以使用Gunicorn做容器 部署flask程序 Gunicorn 是Python WSGI HTTP 服务器 兼容各种web框架不需要配置 安装后直接使用命
  • Java面向对象编程

    在双向循环链表中 在p指针所指的节点后插入一个指针q所指向的新节点 修改指针的操作是 A p gt next q q gt prior p p gt next gt prior q q gt next q B p gt next q p g
  • tensorflow使用显卡gpu进行训练详细教程

    GPU之nvidia smi命令详解查看显卡的信息 cmd nvidia smi GPU之nvidia smi命令详解 简书 编辑 GPU 本机中的GPU编号 有多块显卡的时候 从0开始编号 图上GPU的编号是 0 Fan 风扇转速 0 1
  • Kotlin柯里化——函数调用链

    一 首先看一个小例子 做个铺垫 package net println kotlin chapter5 currying author wangdong description 柯里化 函数调用链 定义一个Hello的方法 传入三个参数 返

随机推荐

  • Android JNI(一):JNI基础概念

    本文讲述 NDK和JNI是什么 JNI的原理 JNI开发流程的步骤 认识JNI相关的代码语法 名称概念 什么是NDK NDK 其中NDK的全拼是 Native Develop Kit Android NDK 就是一套工具集合 允许你使用C
  • 蓝桥杯python组如何准备

    在蓝桥杯的程序设计比赛里新增加了python组 这是一个全新的组别 目前蓝桥杯官网已经开通了python的练习平台 链接http dasai lanqiao cn 如何准备2020年蓝桥杯python程序设计呢 我分为四个部分讲解 了解这四
  • AI无敌?人类的反击静悄悄。

    前几年 alphago横扫围棋棋坛 人类棋手不得不接受现实 那么 按照AI的进步速度 我们当时也提过火车站台的比喻 呼啸而过 望尘莫及 从此 人类棋手输给AI不再是新闻 而且随着相关论文的发布和国内外各个技术团队的跟进 超越顶尖人类棋手的围
  • Python_单下划线和双下划线

    属性 x 公有变量 x 私有变量 在py中不能完全做到私有 只能说 伪私有 只是一种良好的书写习惯 不希望被其他类或者子类访问 x 后置下划线 避免与py中的关键字冲突 方法 fun 私有方法 无法在外部直接访问 只能允许本身访问 子类也不
  • 目的:ubuntu配置使用opengl - 初探-创建一个空窗口

    目的 ubuntu配置使用openGL 初探 创建一个空窗口 环境 系统 Ubuntu18 04 环境 g 步骤 Ubuntu下使用openGL 搭建配置环境并测试窗口 1 openGL库 需要单独安装 由于本机是vmware虚拟机Ubun
  • 关于CittyEngine处于加载界面死机的解决方法

    1 CityEngine License无法打开 与已安装的ArcGISAdministrator冲突 在安装后破解是打开CityEngine License警告 试图执行不支持的操作 提示 CityEngine可以独立安装 并不一定要安装
  • 使用vector对数据进行排序(动态排序)

    排序思路 头函数 algorithm 中有一个函数是 upper bound start end value 它可以返回区间 start end 中第一个大于等于 value 的值的位置 再加上 vector 中自带的插入函数 insert
  • 电脑安装了lattice diamond后,再安装lattice radiant,若出现lattice radiant license checkout failed如何解决?

    我电脑安装了lattice diamond 再安装lattice radiant 设置完环境变量后 发现lattice radiant仍然会报错 如下图 检查环境变量和license都并没有什么错误 但是就是一直会出现以下情况 后面如何解决
  • UML 类图

    1 概述 目录 1 概述 1 1 UML概念 1 2 类图的概念 2 类的表示方式 2 1 普通类 2 2 抽象类 2 3 接口 3 类与类关系的表示 3 1 关联关系 Association 3 1 1 单向关联 3 1 2 双向关联 3
  • 【小程序】如何实现从底部弹出对话框

    前面两篇两篇文章介绍了如何在小程序中实现上下滑动效果以及如何用 Canvas 绘制一张图片 这一篇作为前两篇的延续 介绍如何从底部弹出一个对话框 相比而言 底部弹出对话框的功能比较通用 因此非常适合定义成组件 component 先来看一下
  • 【学习记录贴】Vue+Element-UI富文本编辑框及插入图片

    本贴会涉及以下几个技术点 Vue Element UI实现富文本编辑框 以及文本编辑框中事件拦截 插入图片 Element UI限制上传图片后 隐藏上传按钮 官网上是没有这个方法的 可以通过上传到指定张数后隐藏上传按钮来实现 form表单验
  • MyBatis-Plus删除操作知识点总结

    系列文章目录 Mybatis Plus知识点 MyBatis MyBatis Plus的基础运用 心态还需努力呀的博客 CSDN博客 Mybatis Plus SpringBoot结合运用 心态还需努力呀的博客 CSDN博客MyBaits
  • VScode自由切换输出结果窗口,输出到“终端”和“调试控制台”

    Author xiaozhu sai 软件 Visual Studio Code 点击右边的齿轮按钮 打开launch json文件 注意 console 属性即可 具体见一下代码 使用 IntelliSense 了解相关属性 悬停以查看现
  • C++ sort函数自定义排序规则

    在使用vector容器时经常要进行排序 使用排序函数sort非常方便 但是之前都是简单调用sort v begin v end 没有自定义排序规则使用sort函数的额第三个参数 下面对sort总一个简单总结 头文件 include
  • 计算机网络第2章(物理层)

    B站视频 计算机网络微课堂 有字幕无背景音乐版 网址 https www bilibili com video BV1c4411d7jb p 61 目录 2 1 物理层的基本概念 2 2 物理层下面的传输媒体 导引型传输媒体 非导引型传输媒
  • Vue弹窗传值

    场景 点击新增后 需要将这个页面的分类Id传到弹窗页面 新增的时候绑定这个分类 步骤 1 列表页面中弹窗标签中绑定 classifyId this classify
  • 演唱会为什么总是抢不到票?用Python做一个自动抢票脚本!想看谁的就看谁的!

    大麦网 是中国综合类现场娱乐票务营销平台 业务覆盖演唱会 话剧 音乐剧 体育赛事等领域 但是因为票数有限 还有黄牛们不能丢了饭碗 所以导致了 很多人都抢不到票 那么 今天带大家用Python来制作一个自动抢票的脚本小程序 文章末尾看运行效果
  • Java 基于文本界面的《员工管理系统》

    一 代码实现 1 设计分析 该管理系统使用了5个包 Package 类似于文件夹 1 bean 包含员工类 Employee 2 main 主程序的入口 3 service 主要是 业务逻辑层 的功能实现 4 util 存放工具类 此处存放
  • 【springmvc系】利用RequestBodyAdviceAdapter做接口鉴权

    需求 有个简单的需求 对于第三方接口我们需要做个简单的鉴权机制 这边使用的是非对称性加密的机制 我们提供三方公钥 他们通过公钥对接口json报文使用加密后的报文请求 我们通过对接收过来的请求某一个加密报文字段来进行RSA解密校验 考虑到日后
  • hashmap原理_HashMap原理jdk7和jdk8的区别

    一 hashMap的jdk1 7和jdk1 8区别 1 实现方式 jdk7中使用数组 链表来实现 jdk8使用的数组 链表 红黑树 2 新节点插入到链表是的插入顺序不同 jdk7插入在头部 jdk8插入在尾部jdk7中 如何在头部插入 看a