热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【老实李】HashMap的底层原理探索

通过几个问题来学习HashMap前提大家都知道,HashMap是由哈希表实现的,哈希表就是由数组和链表组成的。给出一个很形象的数据结构图。image.png问题1.既然HashMa

通过几个问题来学习HashMap

前提大家都知道,HashMap是由哈希表实现的,哈希表就是由数组和链表组成的。

给出一个很形象的数据结构图。

《【老实李】HashMap的底层原理探索》 image.png

问题1.既然HashMap是数组+链表实现的,数组开始的时候一定是有一个固定长度的,那HashMap中的数组默认长度是多少呢?

默认情况下,内部数组的长度就是16,这个可以从HashMap的底层源码的构造函数中看到。下面我们就开始HashMap的源码阅读之旅。

HashMap的构造函数有三个。默认我们都是不会传这个initialCapacity的,如果不传的话,那么就会使用默认为4的DEFAULT_INITIAL_CAPACITY

《【老实李】HashMap的底层原理探索》 DEFAULT_INITIAL_CAPACITY
《【老实李】HashMap的底层原理探索》 HashMap的构造函数

然后将initialCapacity的值赋给了threshold,我们会在put方法中判断,如果是第一次put数据就会初始化Table,也就使用到了这个threshold。

《【老实李】HashMap的底层原理探索》 put

那么是怎么初始化的呢?就是拿到这个threshold对其做一下平方。(roundUpToPowerOf2就是对2的幂次幂)

《【老实李】HashMap的底层原理探索》 image.png
《【老实李】HashMap的底层原理探索》 roundUpToPowerOf2就是对2的幂次幂

一步步追踪源代码,发现最后是返回的默认的4的平方的数组长度。

问题2.HashMap既然底层是一个线性的数组,那么是怎么实现的随机存取呢?

(因为是随机存取,所以是有些索引位置是没有元素的,会产生一些空间的浪费,但是这其实就是空间换时间,让HashMap既有数组的查询快,又有链表的增删快的优点)

// 存储时:
int hash = key.hashCode();
int index = hash % Entry[].length;
Entry[index] = value;
// 取值时:
int hash = key.hashCode();
int index = hash % Entry[].length;
return Entry[index];

存储的时候就是拿到key的hash值然后对HashMap底层数组的长度取余,取余的结果就是存储的索引。
取值的时候一样是拿到key的hash值然后对HashMap底层数组的长度取余,得到索引直接去数组里面取就行了。取出来是一个链表的封装类Entry,然后遍历一下Entry中的值取出我们要的就行了。

《【老实李】HashMap的底层原理探索》 image.png
《【老实李】HashMap的底层原理探索》 image.png

总结:元素存储的规则 hash(key)%len, 就是key的hash值对HashMap底层数组的长度取余,这个公式一定要记住。

问题3.通过上面的问题我们了解了HashMap的存取,但是我们要知道Hash算法其实就是把任意长度的输入变换成固定长度的输出,这种转换是一种压缩映射,也就是说,Hash值的空间远小于输入的空间,不同的输入可能会Hash成相同的输出。而且我们又对HashMap默认size 16的数组长度取余,所以不同的key就更是有很大概率返回相同的索引了,那会不会就把之前存的数据给覆盖了呢?

答案当然是否定的。不然谁还敢用HashMap存数据呢。

HashMap我们知道是数组+链表实现的,前面我们是只看到了数组,链表呢就是在这使用的。这里HashMap里面用到链式数据结构的一个概念。上面我们提到过Entry类里面有一个next属性,作用是指向下一个Entry。打个比方, 第一个键值对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这个属性链接在一起。也就是说数组中存储的是最后插入的元素。(数组中只会存一个Entry元素,这一个元素的next就会指向下一个元素,这样循环)

问题4.我们前面看到数组的默认长度是16,可以说很小,那他会不会扩容呢?

答案是肯定的。默认HashMap内部数组的长度为16,负载因子为0.75,就是在构造函数里面传的两个值。阈值就是12(16*0.75=12),这样当第十三个元素加入时,底层数组就会扩容。扩容为原数组大小的两倍。

《【老实李】HashMap的底层原理探索》 image.png

resize(2*table.length)就是扩容的操作。扩容为原数组的两倍。

《【老实李】HashMap的底层原理探索》 扩容为原数组的两倍


推荐阅读
  • 图解HashMap
    什么是HashMap,文章内HashMap源码主要来自Android7.0HashMap是开发中常用的一个类,那么他究竟是什么呢?HashMap是一个存储key-value的集合, ... [详细]
  • 本文深入解析了JDK 8中HashMap的源代码,重点探讨了put方法的工作机制及其内部参数的设定原理。HashMap允许键和值为null,但键为null的情况只能出现一次,因为null键在内部通过索引0进行存储。文章详细分析了capacity(容量)、size(大小)、loadFactor(加载因子)以及红黑树转换阈值的设定原则,帮助读者更好地理解HashMap的高效实现和性能优化策略。 ... [详细]
  • Ihavetwomethodsofgeneratingmdistinctrandomnumbersintherange[0..n-1]我有两种方法在范围[0.n-1]中生 ... [详细]
  • Java集合详解5:深入理解LinkedHashMap和LRU缓存
    Java集合详解5:深入理解LinkedHashMap和LRU缓存今天我们来深入探索一下LinkedHashMap的底层原理,并且使用linkedhashmap来实现LRU缓存。具体代码在我的 ... [详细]
  • HashTable与ConcurrentHashMap均可实现HashMap的功能,对外提供了键值对存储的数据结构。但是在内部结构及实现上有何区别,性能上的差异到底在哪里又是如何导致的 ... [详细]
  • Java之HashMap在多线程情况下导致死循环的问题
    PS:不得不说Java编程思想这本书是真心强大..学习内容:1.HashMap<K,V>在多线程的情况下出现的死循环现象当初学Java的时候只是知道HashMap< ... [详细]
  • 2020年9月15日,Oracle正式发布了最新的JDK 15版本。本次更新带来了许多新特性,包括隐藏类、EdDSA签名算法、模式匹配、记录类、封闭类和文本块等。 ... [详细]
  • MySQL初级篇——字符串、日期时间、流程控制函数的相关应用
    文章目录:1.字符串函数2.日期时间函数2.1获取日期时间2.2日期与时间戳的转换2.3获取年月日、时分秒、星期数、天数等函数2.4时间和秒钟的转换2. ... [详细]
  • 本文节选自《NLTK基础教程——用NLTK和Python库构建机器学习应用》一书的第1章第1.2节,作者Nitin Hardeniya。本文将带领读者快速了解Python的基础知识,为后续的机器学习应用打下坚实的基础。 ... [详细]
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
  • 浅析python实现布隆过滤器及Redis中的缓存穿透原理_python
    本文带你了解了位图的实现,布隆过滤器的原理及Python中的使用,以及布隆过滤器如何应对Redis中的缓存穿透,相信你对布隆过滤 ... [详细]
  • 本文介绍了几种常用的图像相似度对比方法,包括直方图方法、图像模板匹配、PSNR峰值信噪比、SSIM结构相似性和感知哈希算法。每种方法都有其优缺点,适用于不同的应用场景。 ... [详细]
  • 如何将TS文件转换为M3U8直播流:HLS与M3U8格式详解
    在视频传输领域,MP4虽然常见,但在直播场景中直接使用MP4格式存在诸多问题。例如,MP4文件的头部信息(如ftyp、moov)较大,导致初始加载时间较长,影响用户体验。相比之下,HLS(HTTP Live Streaming)协议及其M3U8格式更具优势。HLS通过将视频切分成多个小片段,并生成一个M3U8播放列表文件,实现低延迟和高稳定性。本文详细介绍了如何将TS文件转换为M3U8直播流,包括技术原理和具体操作步骤,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 本文深入探讨了NoSQL数据库的四大主要类型:键值对存储、文档存储、列式存储和图数据库。NoSQL(Not Only SQL)是指一系列非关系型数据库系统,它们不依赖于固定模式的数据存储方式,能够灵活处理大规模、高并发的数据需求。键值对存储适用于简单的数据结构;文档存储支持复杂的数据对象;列式存储优化了大数据量的读写性能;而图数据库则擅长处理复杂的关系网络。每种类型的NoSQL数据库都有其独特的优势和应用场景,本文将详细分析它们的特点及应用实例。 ... [详细]
  • Java 7新功能介绍及与Java1.7性能测试比较
    我们将进行Java7新功能和Java1.7性能测试比较,一般来说Java7新功能主要是对更多类的支持以及加载的架构。而Java7与Java1.5,1.6,1.7的性能测试比较,我们会得出结论, ... [详细]
author-avatar
dmcm0001
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有