对HashMap的理解

一文弄懂HashMap中所有的位运算,理解HashMap设计的精妙! 大家好,我是春风。hashmap经常会被作为我们面试的热身问题。当你讲完自我介绍,面试官上下翻看着你的简历,余光还瞟一眼你的状态和形象。入定,面试官淡淡的说出:请简单讲一下hashmap?或者你理解的hashMap?其实这种泛泛的问法一般是考验面试者的逻辑能力和表述能力,你是否能有条不紊的讲出hashmap的结构,put大致流程等等,回答时除了流程清晰,我们也可以稍微带一点源码上的理解,来体现我们的深度。当然很多情况下,你自己不体现,那面试官会帮你体现~这就是接下来通常会问的hashmap底层原理了,底层原 阅读详情

马上要秋招了,HashMap作为重要的知识点,一直没时间整理,今天晚上室友太吵睡不着,整理一下HashMap的笔记

在我看来,从代码层次呢,分为带参数的和不带参数的;HashMap从版本方面,分为JDK1.7之前,和JDK1.8之后;底层主要使用的方法,就是hashcode和equals方法。JDK1.7之前,使用的是数组和链表的组合,JDK1.8之后,使用的是数组+链表+红黑树的组合。

HashMap的优点:结合了数组和链表两种组合,(JDK1.8后新增了红黑树)这种结构使得HashMap能够提供快速的查找、插入和删除操作

目录

一、先从代码构造方法入手

1.无参构造

 2.带参构造——第一个参数:底层数组的长度;第二个参数:加载因子

二、版本方面:分为JDK1.7之前的,和JDK1.8之后的;

1.以JDK1.7为例展开介绍

1.1 一般的差异和问题都是围绕 增加展开的;

1.1.1  增加时,键(key)不能相同,如果相同就会发生覆盖,成为“改”,返回值就是被覆盖的值(value)。

1.1.2  在底层代码中,每个key都有对应的哈希值(hashcode()),根据哈希值得到在数组的索引,根据索引找到位置

源码:通过二次散列和扰动函数,增加哈希值的随机性和分布的均匀性,减少哈希冲突的机会

具体过程:

1.1.3 数组的扩容:

条件:(同时满足)

过程:

特殊情况:

在扩容条件这一块,有两种极端情况:

扩容过程时候启用了多线程:

1.2  modCount:追踪结构性修改的次数

用途一:在循环的时候不可以使用集合的方法来进行元素的添加(put)或者删除(remove)

用途二:避免并发修改—— 当多个线程对集合进行操作时,一条线程去添加元素,另一条去删除元素,最终肯定会出现问题,modCount可以提前停止程序的运行,并抛出异常ConcurrentModificationException。


一、先从代码构造方法入手

1.无参构造

        HashMap<String,String> hm = new HashMap<>();

 2.带参构造——第一个参数:底层数组的长度;第二个参数:加载因子

        HashMap<String,String> hm = new HashMap<>(16,0.75F);

解释:这两个参数都是定义一个底层数组长度为16,加载因子为0.75的HashMap双列链表,这个链表的泛型为 String类型,也就是<key,value>的类型。注意:加载因子是扩容条件之一。


二、版本方面:分为JDK1.7之前的,和JDK1.8之后的;

1.以JDK1.7为例展开介绍

1.1 一般的差异和问题都是围绕 增加展开的;

hm.put("a","App");
hm.put("b","Box");
1.1.1  增加时,键(key)不能相同,如果相同就会发生覆盖,成为“改”,返回值就是被覆盖的值(value)。
hm.put("b","Boy"); //覆盖key="b"的值,并将原本的值返回,
1.1.2  在底层代码中,每个key都有对应的哈希值(hashcode()),根据哈希值得到在数组的索引,根据索引找到位置
源码:通过二次散列和扰动函数,增加哈希值的随机性和分布的均匀性,减少哈希冲突的机会
  • 计算哈希值——hash()方法
    final int hash(Object k) {
        int h = 0;
        if (useAltHashing) {
            if (k instanceof String) {
	            //对字符串键使用备选哈希函数
                return sun.misc.Hashing.stringHash32((String) k);
            }
            //随机种子,用来降低冲突发生的几率
            h = hashSeed;
        }
        //二次散列:增加哈希值的随机性和分布的均匀性
        h ^= k.hashCode();

        //扰动函数
        //混合高低位,极大的避免了因为低位的相同而导致的哈希冲突。
        //由于最后要和数组长度 相与,数组的长度一般不会超过4位二进制
        //所以一般用的就是低四位二进制位,如果不进行下面操作,
        //一个hashCode()得出的是32位二进制位,就会有 2^18 种可能性会发生哈希冲突
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
  •  根据哈希值计算索引——indexFor()方法:
   static int indexFor(int h, int length) {
	    //把hash值和数组的长度进行“与”操作
        //保证计算出的索引在 数组索引范围内
        return h & (length-1);
    }
具体过程:
  • 如果这个位置(索引处)没人(元素),直接坐下(放进数组)。
  • 如果这个位置有人了,调用equals方法,对比一下key值,如果两个key相同,直接将值value覆盖;如果不同(也就是发生了哈希碰撞),霸占他的位置,让他坐下一排(头连接:新元素放在数组中,老元素作为链表元素连接到新元素后面);
1.1.3 数组的扩容:
条件:(同时满足)
  • 当前数组中的元素数量 >= 数组的阈值(阈值 = 数组的长度 * 加载因子——> 16 * 0.75 = 12),也就是超过了12;
  • 当前要存的位置已经有元素了,且两者的键不同(哈希碰撞)。
过程:
  • 以原数组长度的两倍(16 * 2 = 32) 去创建一个新数组;
  • 将老数组的元素从0索引开始,有链表先算链表,从链表头元素依次向下取,等链表结束后,再按照数组索引向下取元素,取出来的元素依次重新计算哈希值,再放入新数组中。
void transfer(Entry[] newTable, boolean rehash) {
    int newCapacity = newTable.length;
    for (Entry<K,V> e : table) {
        while(null != e) {
            Entry<K,V> next = e.next; // 保存下一个节点的引用,防止在移动节点过程中断链
            if (rehash) { //一般为false
                e.hash = null == e.key ? 0 : hash(e.key); // 重新计算hash值
            }
            int i = indexFor(e.hash, newCapacity); // 计算在新table中的位置
            e.next = newTable[i]; // 将e节点指向新table的头节点
            newTable[i] = e; // 将新table的头节点指向e
            e = next; // 移动到下一个节点
        }
    }
}
特殊情况:
在扩容条件这一块,有两种极端情况:
  • 第一种,可能存16个元素,正好16个元素哈希值都不同,且索引也不同,这样当存入第17个元素时,才会发生了哈希碰撞,触发扩容机制。
  • 第二种,最多存16+15 = 31个,正好前11个元素全部发生哈希碰撞,此时小于阈值不满足第一个条件,然后后面15个元素都没有发生哈希碰撞,不满足第二个条件,当存入下一个的时候,才会触发扩容机制
扩容过程时候启用了多线程:

问题:由于老数组扩容存入新数组时候,需要重新计算哈希值,然后放入老数组,但是有可能老数组中同一条链表上的元素,被倒序重新放入了新数组中(头连接,后来者居上);当两条线程同时进行数组扩容时候,第一条线程扩容完后,第二条线程的next和e的指向的变反了,导致出现环路,报出异常。

参考文档:

详细https://www.cnblogs.com/seeall/p/18063073icon-default.png?t=O83Ahttps://www.cnblogs.com/seeall/p/18063073简单:HashMap多线程扩容导致死循环解析(JDK1.7)_hashmap1.7 扩容死循环问题-优快云博客文章浏览阅读616次,点赞11次,收藏12次。由于头插法的原因,再次执行头插法的时候会将原来的链表顺序反过来,所以当第一个线程执行完扩容之后会将顺序从。最后由于e等于null,退出循环,当下次遍历到该元素时就会进入死循环。1->2->3 到 3->2->1。线程二第一次执行插入结束后。线程二准备第二次循环结束后。线程二第二次执行插入结束后。线程二准备第二次循环结束后。线程二第三次执行插入结束后。线程二准备第三次循环结束后。_hashmap1.7 扩容死循环问题https://blog.youkuaiyun.com/qq_51902025/article/details/135628412

解决方法:

  • 第一种:防止扩容——创建HashMap的同时设置合适的数组长度和加载因子。
  • 第二种:在开启多线程的同时加锁——synchronized

 


 

1.2  modCount:追踪结构性修改的次数

用途一:在循环的时候不可以使用集合的方法来进行元素的添加(put)或者删除(remove)
  • 原因:在JDK1.7中,HashMap在这个版本中通过modCount字段来追踪结构性修改的次数,调用迭代器方法时(增强for循环编译成class文件时也会转换成迭代器的方法,Lambda表达式底层也是迭代器或增强for循环),会将modCount 赋值给expectedModCount,每次调用迭代器中的next()方法,都会检查expectedModCount和modCount是否相等,如果不相等则抛出ConcurrentModificationException异常。
  • 解决方法:需要在遍历的同时对元素进行添加或者删除,需要调用迭代器(Iterator)中的方法。其内部会对expectedModCount重新赋值。
用途二:避免并发修改—— 当多个线程对集合进行操作时,一条线程去添加元素,另一条去删除元素,最终肯定会出现问题,modCount可以提前停止程序的运行,并抛出异常ConcurrentModificationException。

JDK1.8下一章再写吧,一点这个写了4个小时,中间电脑蓝屏了一次,打开后全没了,还以为白写了2多小时,没想到自动保存草稿了,嗯~很nice。

奥顺居PHP测算引流SEO程序_八字风水_起名解梦_星座生肖综合门户源码 奥顺居PHP测算引流SEO程序_八字风水_起名解梦_星座生肖综合门户源码 S30新框架适合多种奥顺插件。 阅读详情

相关推荐

无需云服务器:飞牛云NAS+Docker搭建本地WordPress站点全球访问

在数字化浪潮中,网站搭建已成为个人和企业在推广自身品牌与业务的首要选择。飞牛云新推出的fnOS,这款集多功能于一身的nas系统,以其卓越的性能和灵活性,成为理想的本地网站部署平台。本文将带你一探如何在飞牛云NAS上通过Docker部署WordPress,并利用cpolar实现全球访问。

LT15171009269的博客 6528

HashMap详解

一、HashMap集合简介 HashMap 基于哈希表的 Map 接口实现,是以 key-value 存储形式存在,即主要用来存放键值对。HashMap 的实现不是同步的,这意味着它不是线程安全的。它的 key、value 都可以为 null,此外,HashMap 中的映射不是有序的。 jdk1.8 之前 HashMap 由 数组 + 链表 组成,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突(两个对象调用的 hashCode 方法计算的哈希值经哈希函数算出来的地址被别的元素占用)而存

java123456111的博客 5万+

国科GK7202V300芯片开发避坑指南:从uboot编译到SDK配置的12个实战问题解决

本文针对国科GK7202V300芯片开发过程中的常见难题,提供从uboot编译、SDK配置到系统部署的实战解决方案。详细解析了开发环境搭建、内核编译、驱动模块加载、根文件系统构建及烧写部署等12个关键环节的避坑指南,帮助开发者高效解决编译错误、配置冲突等典型问题,提升开发效率。

sunny的博客 234

HashMap的常见用法

文章目录1、常用方法示例2、3种遍历方式3、定义的时候初始化4、getOrDefault(Object key, V defaultValue)5、putIfAbsent(K key, V value) 1、常用方法示例 Map<String,Integer> map=new HashMap<>();//Map父类 map.put("zhangsan", 10); map.put("lisa", 20); map.put("wangwu", 50);

qq_41571459的博客 4553

手机热点+Advanced IP Scanner:5分钟搞定树莓派无屏SSH连接(附详细配置截图)

本文详细介绍如何利用手机热点和Advanced IP Scanner工具快速实现树莓派的无屏SSH连接。通过简单的WiFi配置和IP扫描,5分钟内即可完成连接,特别适合外出调试或空间有限的工作环境。文章包含详细步骤、配置截图和常见问题解决方案,帮助开发者高效管理树莓派设备。

ik678901234的博客 952

一篇讲透:豆包、元宝、DeepSeek、Kimi、WorkBuddy,职场里到底怎么分工

这篇文章深入分析了当前主流AI工具(WorkBuddy、豆包、腾讯元宝、DeepSeek、Kimi)的特点和使用场景,帮助读者根据不同工作需求选择合适的AI助手。核心观点包括: AI工具各有所长:豆包适合快速创作初稿,元宝擅长信息检索整理,DeepSeek强于逻辑分析,Kimi专精文本处理,WorkBuddy则更适合任务推进和成果交付。 使用前需明确需求:是要创意发散、信息整理,还是任务执行?不同阶段应选用不同工具组合。 提供了6个具体职场场景的解决方案,包括文章创作、课程设计、会议纪要等,并给出详细的操作

weixin_39284145的博客 1万+

HashMap(常用)方法

文章目录 Hashmap的存值: Hashmap的取值: Hashmap的判断为空 Hashmap判断是否含有key: Hashmap判断是否含有value: Hashmap删除这个key值下的value: Hashmap显示所有的value值: Hashmap的元素个数: Hashmap删除这个key值下的value: Hashmap显示所有的key和value: Hashmap添加另一个同一类型的map下的所有值: Hashmap删除这个key和valu

Hningning的博客 1580

SY6982C 7.4V锂电池1A充电模块原理图设计,已量产

在手持设备、便携工具与小型无人机等场景中,7.4V(2S)锂聚合物电池的应用愈发广泛。这类电池对充电系统的安全性、效率与兼容性提出了明确要求:需支持 USB 5V 输入、1A 安全充电,同时具备多重保护机制。本文将结合一款基于 SY6982C 的充电原理图,深入解析其电路设计与技术亮点。

不脱发的程序猿 360

Java 集合系列之 HashMap详细介绍(源码解析)和使用示例

概要 这一章,我们对HashMap进行学习。 我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap。内容包括: 第1部分 HashMap介绍 第2部分 HashMap数据结构 第3部分 HashMap源码解析(基于JDK1.6.0_45)     第3.1部分 HashMap的“拉链法”相关内容     第3.2部分 HashMa

fivehundredyearsago的博客 938

【100%通过率】2023华为od机试C卷【电脑病毒感染】C语言 实现

一个局域网内有很多台电脑,分别标注为 0 ~ N-1 的数字。相连接的电脑距离不一样,所以感染时间不一样,感染时间用t 表示。其中网络内一台电脑被病毒感染,求其感染网络内所有的电脑最少需要多长时间。如果最后有电脑不会感染,则返回-1.

MISAYAONE的博客 1042

Android Shadow插件化框架分析与集成(一)

到此为止,本篇文章先介绍到这里,本篇文章主要介绍了:从源码的角度去集成shadow框架,从下载源码开始,到集成到自己的项目中,其中插件项目我是新建的demo应用,宿主应用是之前搭的一个MVP框架。文章中一步一步详细的介绍了宿主该如何集成,并提供了源码;介绍了插件该如何集成,同时也提供了源码。文章中还介绍了gradle的版本与AGP版本使用不当造成的问题,以及shadow源码的gradle版本与APG版本的配置,及和宿主插件应用gradle版本与AGP版本的关系。

superzhang6666的博客 1234

HashMap详解 Java [ 基础 ] ✅

HashMap是Java中一个强大且高效的集合类,用于快速查找和存储键值对。理解其工作原理和常用操作对于提高编程效率和解决复杂问题非常有帮助。

lenyan~的博客 2万+

让openclaw接入web端的deepseek或着qwen,使用无限token

🪟 Windows: 提供原生的 PowerShell 一键安装支持!配置过程和官方版本是相同的,唯一的区别就是再选则model时要选择带Browser字样的deepseek或者qwen。安装之前需要先清理官方版本的安装痕迹,否则openclaw版本跳转不到这个中文版本。因为此版本与官方版本存在代差,在配置飞书时可能会出现无法安装飞书的情况。剩下的配置保持和官方版本一致操作即可。选中之后需要进行一次网页端的登录。登录后会出现可选择的model。安装完成后会跳到这个中文界面。涉及到接入飞书机器人的操作。

z1872385的博客 302

【HashMap全面知识点】— 快速理解HashMap

本文讲解了 HashMap 的核心知识,包括其定义(实现 Map 接口,存键值对,允许 null 键值,非线程安全且无序)与核心参数(容量默认 16 且为 2 的幂、负载因子默认 0.75 等)。还阐述底层数据结构演进(JDK1.7 为数组 + 单向链表,JDK1.8 新增红黑树)、核心算法(哈希值与索引计算、put 方法流程、扩容机制),对比 JDK1.7 与 1.8 差异,分析并发隐患及解决方案,列举高频面试题,并给出实践建议,帮助读者全面理解 HashMap。

Mr.Ja的博客 2205

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

HashMap源码理解

HashMap源码的理解

尹凯文的博客 2829

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

对hashmap的理解

参考文章:https://www.cnblogs.com/liujinhong/p/6576543.html 参考文章:https://blog.youkuaiyun.com/qq_42034205/article/details/90384772 一直没有深入理解hashcode的hash原理,今天看了别人的博客,恍然大悟,这里记录下,希望能多保留几天印象。。。 jdk1.8版本 实现原理是,...

birdofsky的专栏 248

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

HashMap的理解

HashMap为什么是线程不安全的?     一直以来只是知道HashMap是线程不安全的,但是到底HashMap为什么线程不安全,多线程并发的时候在什么情况下可能出现问题?       HashMap底层是一个Entry数组,当发生hash冲突的时候,HashMap是采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。         java

Kitty_Landon的专栏 489

hashMap的理解

hashMap的理解一、HashMap简介put原理get原理二、哈希碰撞三、与HashTable的区别1. HashTable介绍2. 区别最后、常见问题一、HashMap中的“死锁”是怎么回事 一、HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,无序,不可重复。 HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。 HashMap 实现了Serializable接口,因此它

weixin_45671337的博客 2911

深入理解HashMap

版权声明:本文为博主转载文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.youkuaiyun.com/Woo_home/article/details/103146845 什么是 HashMap? ​ HashMap 是基于哈希表的 Map 接口是实现的。此实现提供所有可选操作,并允许使用 null 做为值(key)和键(valu...

Mr__fang的专栏 269

HashMap的底层简单理解

概述 HashMap基于Map接口实现,元素以键值对的方式存储,并且允许使用null建和null值,因为key不允许重复,因此只能有一个键为null,另外HashMap不能保证放入元素的顺序,它是无序的,和放入的顺序并不能相同。HashMap是线程不安全的。 数据结构 HashMap底层是以数组+链表方式进行存储,通过对key的hash计算和数组长度得到value在数组中存储的位置(index = hash值 & (length - 1))。当不同的ke...

weixin_42640226的博客 2742

HashMap 的理解

HashMap源码分析 基于JDK7的HashMap源码分析 类的介绍 下面的类介绍是从源码的英文翻译来的 HashMap是基于哈希表实现的Map接口实现类。这个实现提供所有的map相关的操作,允许使用null的键和null的值。(HashMap与Hashtable大致是一样的,只是HashMap是不同步的,且它允许你null的键和值。);另外,HashMap内部元素排列是无序的。 假设...

程序领域 257

HashMap的一些自己的理解

HashMap的一些自己的理解 HashMap是实现Map接口,底层是采用了哈希表(散列表)结构, 既然说道了哈希表,那就说下哈希表的结构 我们都知道, 数组的特点是占用空间连续, 查询效率高, 增删效率低;链表结构占用空间不连续, 查询效率低, 增删效率高, 我们能不能既查询效率高, 又增删效率也高呢? 为此我们就有了哈希表 哈希表就是结合了数组和链表的优点, 增删查效率高的一种数据结构 Has...

qq_43413036的博客 171

HashMap原理深入理解

HashMap原理深入理解

cg_Amaz1ng的博客 233

【HashMap】源码理解泳道图

hashMap 源码理解

上善若水 1014

带你逐步实现简易HashMap,助力理解Java HashMap

一、实现简易HashMap 作为讲解用,不可用作生产环境! 第一步,把key实体转化为一定范围内的数字。 我们知道Java中所有的数据类型可以以对象的形式呈现的。 而且它为每个对象赋予了一个编号(即 hashCode()函数的返回值) 这样我们就能使用某种方法将这个代表对象的编号转化为一个一定范围内的数字。(为了讲解方便我们暂时把这个范围设置为0~9) private int hash(Objec...

weixin_33768481的博客 184

HashMap之put方法源码理解总结

引入 在理解hashmap的put方法之前,需要对hashmap有一些基础的理解。 **1.hashmap的数据结构:**数组 + 链表 + 红黑树 2.put数据的大概流程: 1.首次put数据 当定义了一个hashmap结构,在第一次调用put方法进行,添加数据的时候,首先会对hashmap结构进行初始化,生成长度为16的数组。 然后插入的数据调用hash(key)生成hash值,用来定位数据...

FortressBesieged的博客 308
上一篇: Web开发中MySQL数据库的使用
下一篇: MyBatis ——在java层面对MySQL数据库进行操作
new2MySQL
博客等级 码龄5年 255粉丝 23原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值