如果你这么去理解HashMap就会发现它真的很简单
Java中的HashMap相信大家都不陌生,也是大家编程时最常用的数据结构之一,各种面试题更是恨不得掘地三尺的去问HashMap、HashTable、ConcurrentHashMap,无论面试题多么刁钻的问,只要我们真正的掌握了它的设计思想,便可以不变应万变,hold住所有的面试题了。
本文主要包含以下内容,力求深入浅出一步一步彻底明白HashMap的设计思想:
- 数组的优势
- 数组是特殊的键值对
- Hash函数
- Hash冲突
- 此时再看HashMap源码
文章干货内容较多,建议大家“收藏”后持续阅读!
关注“java架构设计”,阅读更多技术干货文章!
数组的优势
整型数组
上图是一个含有8个元素的整型数组,数组下标从0到7,
如果我们要获取第四个元素的值,直接a[3]就可以了,时间复杂度为O(1),
如果我们要将第四个元素的值替换为36,直接a[3]=36就可以了,时间复杂度也是O(1),
也就是说基于下标的随机访问和赋值数组元素的时间复杂度都是O(1),无论这个数组是多大(在内存充足的条件下),这是数组的优势之一。
数组不够,我们还需要键值对
通过上面的简单描述,我们可以知道数组可以通过“下标”来获取数组中的指定元素,但是这个下标只能是正整数,且从0开始。
但是如果我们想通过一个浮点数、一个字符串、一个对象来获取对应的元素呢?也就是所谓的键值对,是不是数组就满足不了了?
我们可以把数组看做是一种特殊的键值对,key就是数组的下标,value就是下标对应的元素:
键值对
所以我们要求KV数据结构里面的key是一个对象,而不仅仅只能是数组中的一个下标。因此我们需要创造出一种数据结构,他至少需要具有以下的特性:
- O(1)复杂度的访问任何一个key对应的值
- 这个key可以支持整型、浮点、字符串、对象等任何类型的数据
第一个特性要求我们需要一个像数组下标一样的整型数字来快速访问数组。
第二个特性要求我们的下标不限制只能是整型。
显然我们需要对key做一些特殊处理,这个时候Hash函数就上场了。
Hash函数
哈希函数的作用就是通过哈希算法把任意类型的key转换成固定类型、固定长度的散列值,也就是我们所期望的数组下标(整型)。
因此哈希函数需要具有如下的特征:
- 相同的内容经过哈希算法计算后输出结果一致;
- 不同的内容经过哈希算法计算后输出不同的结果,但也可能会出现相同的输出值(即哈希碰撞);
因此,一个优秀的哈希算法是不同的内容经过哈希计算后输出的结果具有分布均匀的特点,也就是低碰撞率。
同时,计算速度必须要快!
比较出名的哈希算法有time33算法、Murmurhash算法,这些算法都在追求更好的均匀分布和更快的计算速度。
Java8中java.lang.String类的hashCode方法实现:
java.lang.String#hashCode()
现在来写一个简单的测试类来测试Java中的String类实现的hashCode:
输出:
s1.hashCode=92599395s2.hashCode=92599395s3.hashCode=92599396可以看出来s1和s2是相同的字符串,输出了相同的hash值,s3和s1、s2不同,输出的hash值也不同,但是也很接近。
说明java采用的hash算法分散性不好,如果用Murmurhash算法,差异就会很大,即哈希算法的剧烈度大,感兴趣的同学可以用Guava中Murmurhash方法试验一下。
通过Hash算法,我们可以计算出任何一种数据类型的哈希值且为整型,这样就满足了数组的下标必须为整数的要求了。
但是又来了一个问题,通过上面的实验我们拿到的hashCode值很大,无法作为数组的下标,否则我们的数组占用的内存就太大了!
所以就采用了根据hashCode取余的方式,比如Java中的HashMap默认size是16,那么92599395%16=3,那么实际上abcde这个字符串就存储在HashMap数组中下标为3的地方。
Hash冲突
上面已经讲过Hash算法无法做到完全均匀分布,也就是说可能会有那么两个不一样的字符串经过hash计算后得到相同的值,此时两个不同的字符串都得对应同一个数组下标上,这就造成了所谓的Hash冲突。
因此,为了解决Hash冲突问题,我们需要下标对应的元素不再仅仅是当前对应的字符串了,而应该是当前的字符串再加上它的next节点的对象地址,这样的一个对象应该如下:
当根据key去找值时候,先计算出key的hash值再取余得到数组的下标,然后根据下标获取到元素,再判断该元素的key是否和当前的key相等(如何判断相等?equals方法!),如果不相等,继续取next节点,继续判断。
说到这里大家如果对HashMap熟悉的话,是不是发现这其实就是HashMap的简单版,一个数组+链表的实现:
再看HashMap源码
如果你已经看到这里,那么我相信你一定已经了解了HashMap的结构了,那么请回去看HashMap的源码吧,你会发现原来是这么简单!这个时候你已经达到了“看山不是山”的境界了!面试官问你任何关于HashMap的问题相信你都能回答了。
搞清楚了HashMap之后,HashTable和HashMap的区别?ConcurrentHashMap是线程安全的基于分段锁的HashMap?为了优化链表的性能,当链表的数超过8之后就变成平衡树了等等。。。
后面做的事情要么是为了线程安全要么就是为了性能。
如果你这么去理解HashMap就会发现它真的很简单的更多相关文章
- JSON:如果你愿意一层一层剥开我的心,你会发现...这里水很深——深入理解JSON
		我们先来看一个JS中常见的JS对象序列化成JSON字符串的问题,请问,以下JS对象通过JSON.stringify后的字符串是怎样的?先不要急着复制粘贴到控制台,先自己打开一个代码编辑器或者纸,写写看 ... 
- 深入理解HashMap
		转自:http://annegu.iteye.com/blog/539465 Hashmap是一种非常常用的.应用广泛的数据类型,最近研究到相关的内容,就正好复习一下.网上关于hashmap的文章很多 ... 
- 集合之深入理解HashMap
		Hashmap是一种非常常用的.应用广泛的数据类型 1.hashmap的数据结构 要知道hashmap是什么,首先要搞清楚它的数据结构,在java编程语言中,最基本的结构就是两种,一个是数组,另外一个 ... 
- 深入理解HashMap(原理,查找,扩容)
		面试的时候闻到了Hashmap的扩容机制,之前只看到了Hasmap的实现机制,补一下基础知识,讲的非常好 原文链接: http://www.iteye.com/topic/539465 Hashmap ... 
- 深入理解HashMap(及hash函数的真正巧妙之处)
		原文地址:http://www.iteye.com/topic/539465 Hashmap是一种非常常用的.应用广泛的数据类型,最近研究到相关的内容,就正好复习一下.网上关于hashmap的文章很多 ... 
- 深入理解HashMap(jdk7)
		HashMap的结构图示  jdk1.7的HashMap采用数组+单链表实现,尽管定义了hash函数来避免冲突,但因为数组长度有限,还是会出现两个不同的Key经过计算后在数组中的位置一样,1.7版本 ... 
- Map 综述(一):彻头彻尾理解 HashMap
		转载自:https://blog.csdn.net/justloveyou_/article/details/62893086 摘要: HashMap是Map族中最为常用的一种,也是 Java Col ... 
- 从逆向的角度去理解C++虚函数表
		很久没有写过文章了,自己一直是做C/C++开发的,我一直认为,作为一个C/C++程序员,如果能够好好学一下汇编和逆向分析,那么对于我们去理解C/C++将会有很大的帮助,因为程序中所有的奥秘都藏在汇编中 ... 
- JAVA中的数据结构  -  真正的去理解红黑树
		一, 红黑树所处数据结构的位置: 在JDK源码中, 有treeMap和JDK8的HashMap都用到了红黑树去存储 红黑树可以看成B树的一种: 从二叉树看,红黑树是一颗相对平衡的二叉树 二叉树--&g ... 
随机推荐
- thinkphp 中区块block和模板继承extend用法举例
			1.介绍 模板继承其实并不难理解,就好比类的继承一样,模板也可以定义一个基础模板(或者是布局),并且其中定义相关的区块(block),然后继承(extend)该基础模板的子模板中就可以对基础模板中定义 ... 
- CCNA 第四章 轻松划分子网
			1:划分子网的的好处: (1):减少网络流量 (2):优化网络性能 (3):简化管理 (4):有助于覆盖大型地理区域 2:CIDR和ISP的概念 (1):CIDR:Classless Inter-Do ... 
- TLB和CPU缓存
			TLB 如果每次应用程序访问一个线性地址都需要先解析(查PDT,PTT)那么效率十分低,为了提高执行效率CPU在CPU内部建立了一个TLB表,此表和寄存器一样访问速度极高.其会记录线性地址和物理地址之 ... 
- 分析型CRM系统都分析什么?
			在之前的文章中我们曾经讲过,目前市面上常见的CRM系统大概可以分为通用型.协助型和分析型三种类型.由于每个企业的类型.业务的不同,就需要选择一款适合的CRM客户关系管理系统.今天我们就来说一说,分析型 ... 
- 加载usbserial驱动后,为什么adb不可用了?
			某设备提供了USB串口功能,上位机(Host端)可以通过USB串口与之通信.对于Linux上位机,比如Ubuntu,自带usbserial驱动,当安装usbserial驱动后,上位机就会生成ttyU ... 
- ElasticSearch7使用指导
			目录结构: 一.es概述 二.es安装/head插件安装/kibana安装 三.es核心概念 四.IK分词器 五.RestFul操作 六.CRUD(增删改查) 七.Springboot集成es --- ... 
- [Java] javaEE
			定义 面向企业级应用中一些通用模块制定的标准 避免重复开发,解决代码可靠性问题 13种规范 JDBC(JavaDatabase Connectivity):数据库连接 以统一方式访问数据库的API J ... 
- Docker------Idea连接远程并生成和上传镜像
			1.Docker开启远程访问连接 备注: 1)Linux是CentOS7版本 2)安装Docker可参考: https://www.cnblogs.com/tianhengblogs/p/125202 ... 
- [转载]有些shell文件中为啥要用$(cd “$(dirname $0)“; pwd),pwd它不香吗
			$(cd "$(dirname "$0")",pwd) 解析 xx.sh 文件内容如下: #!/bin/bash BIN_FOLDER=$(cd " ... 
- coverage report
			转载:http://blog.sina.cn/dpool/blog/s/blog_7853c3910102yn77.html VCS仿真可以分成两步法或三步法, 对Mix language, 必须用三 ... 
