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

HashMap 的实现原理

说一下 HashMap 的实现原理?

JDK1.7

HashMap的主干是一个Entry数组。Entry是HashMap的基本组成单元,每一个Entry包含一个key-value键值对。(其实所谓Map其实就是保存了两个对象之间的映射关系的一种集合),其中Key 和 Value 允许为null。这些个键值对(Entry)分散存储在一个数组当中,HashMap数组每一个元素的初始值都是Null。

骚戴理解:HashMap不能保证映射的顺序,插入后的数据顺序也不能保证一直不变(如1.8以前的扩容操作会导致顺序变化)

Entry是HashMap中的一个静态内部类.

  static class Entry<K,V> implements Map.Entry<K,V> {final K key;V value;Entry<K,V> next;//存储指向下一个Entry的引用,单链表结构int hash;//对key的hashcode值进行hash运算后得到的值,存储在Entry,避免重复计算/*** Creates new entry.*/Entry(int h, K k, V v, Entry<K,V> n) {value = v;next = n;key = k;hash = h;} 

HashMap的总体结构如下

骚戴理解:简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。

JDK1.8

JDK 1.8 中 HashMap 的底层原理是采用数组+链表+红黑树的结构实现的,主要包括以下几个方面:

1. 数组:HashMap 内部维护了一个数组,用于存储键值对。数组的长度是固定的,且必须是 2 的幂次方。

2. 链表:数组中的每个元素都是一个链表的头结点,用于存储哈希值相同的键值对。当链表长度超过8(TREEIFY_THRESHOLD - 阈值)时,链表就自行转为红黑树,以提高查找效率。

3. 红黑树:当链表长度超过一定阈值时,HashMap 会将链表转换为红黑树,以提高查找效率。红黑树是一种自平衡二叉查找树,它的查找、插入、删除等操作的时间复杂度都是 O(log n)。

4. 扩容:当 HashMap 中的元素个数超过数组长度的 75% 时,HashMap 会进行扩容操作,即将数组长度扩大一倍,并重新计算每个元素在新数组中的位置。扩容操作会导致所有元素的位置发生变化,因此需要重新计算每个元素在新数组中的位置,这个过程比较耗时。

5. hash 函数:HashMap 会使用键对象的 hashCode() 方法计算出键的哈希值,然后根据哈希值计算出键值对在数组中的位置。

6. 线程安全:JDK 1.8 中的 HashMap 是线程不安全的,如果多个线程同时对 HashMap 进行写操作,可能会导致数据丢失或者出现死循环等问题。如果需要在多线程环境下使用 HashMap,可以使用 ConcurrentHashMap。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • CSS3动画——飞行的小精灵
  • webpark 如何将本地访问地址http://localshot:3000修改为自己需要的访问地址https://www.example.com:3000
  • 【ES6】使用Proxy实现单例模式
  • 论文阅读:scMGCA----模型方法
  • Go语言Time包的使用
  • C. Propagating tree
  • HTML5 浏览器支持
  • XML CSS:结构和样式的完美结合
  • 秋招突击——8/16——字节广告业务——面经整理——二面挂
  • 【docker compose 部署和 go 热部署工具fresh】
  • git rebase和git merge的区别
  • EazyDraw for Mac 矢量图绘制设计软件
  • MySQL 学习笔记之事务操作
  • yd云手机登录算法分析
  • EXCEL 分段排序--Excel难题#86
  • .pyc 想到的一些问题
  • Essential Studio for ASP.NET Web Forms 2017 v2,新增自定义树形网格工具栏
  • Git初体验
  • Go 语言编译器的 //go: 详解
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • java2019面试题北京
  • Just for fun——迅速写完快速排序
  • NLPIR语义挖掘平台推动行业大数据应用服务
  • springMvc学习笔记(2)
  • Vue 重置组件到初始状态
  • 基于Mobx的多页面小程序的全局共享状态管理实践
  • 数据可视化之 Sankey 桑基图的实现
  • puppet连载22:define用法
  • 昨天1024程序员节,我故意写了个死循环~
  • ​​​【收录 Hello 算法】9.4 小结
  • ​创新驱动,边缘计算领袖:亚马逊云科技海外服务器服务再进化
  • #define,static,const,三种常量的区别
  • (1)常见O(n^2)排序算法解析
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第2节(共同的基类)
  • (poj1.3.2)1791(构造法模拟)
  • (zz)子曾经曰过:先有司,赦小过,举贤才
  • (算法)求1到1亿间的质数或素数
  • (图文详解)小程序AppID申请以及在Hbuilderx中运行
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • .NET Core 和 .NET Framework 中的 MEF2
  • .net core使用RPC方式进行高效的HTTP服务访问
  • .net 托管代码与非托管代码
  • .NET/C# 获取一个正在运行的进程的命令行参数
  • @requestBody写与不写的情况
  • @selector(..)警告提示
  • []串口通信 零星笔记
  • [16/N]论得趣
  • [2019.3.20]BZOJ4573 [Zjoi2016]大森林
  • [AIGC codze] Kafka 的 rebalance 机制
  • [ARC066F]Contest with Drinks Hard
  • [bug总结]: Feign调用GET请求找不到请求体实体类
  • [C++]入门基础(1)
  • [codevs] 1029 遍历问题
  • [C语言]——柔性数组
  • [ERROR ImagePull]: failed to pull image k8s.gcr.io/kube-controller-manager失败