Java 集合Map攻略1:HashMap

Java 集合Map三把刀1:HashMap

每次面试经常会遇到关于集合方面的问题,大部分情况我都是扫下网上关于集合方面的简单的介绍,简短的描述一下集合里线程安全有哪些线程不安全有哪些,然后就完了,面试的时候自我感觉还很良好,其实不知道一首凉凉已经自己送给自己,哈哈哈

言归正传:Map 
	谈到集合,肯定要聊聊他们的数据结构,数据结构是其最核心的部分,也是大部分面试官区分你是不是真了解还是一知半解

三种Map底层都是Entry<K,V> 唯一不同的是Entry里面的数据结构.
HashMap:数据结构是数组+单向链表的组合,这种组合挺有意思的,链表其实在Entry<k,v>里,里面有一个next元素,根据这个元素来进行链表维护,外部是一个Entry数
文字赘述太烦看图比较直接

这里写图片描述

key value没啥好说的,主要是这个next链表,这玩意儿卡了我半天,有些文章介绍的比较抽象,查阅了很多篇文章终于明白这玩意儿是干啥的了

首先,HashMap是散列表,不要以为他用了数组存储就老老实实的顺序存了,其实存储还是无序的.
怎么存的呢?大家就要看源代码里的put方法了,继续上图

这里写图片描述

大家主要看着三个地方基本上就能理解hashmap的存储结构了

1 hash(key)
这玩意干嘛的呢,看名字计算出对应的hash值

2 indexFor
这干啥呢,这个的作用是根据对应的hash值计算出对应的下标,这个下标对应着数组的下标,实际存储的数组位置

3 for循环
上面俩很好理解,最后一个就需要多花两分钟了.这里也正是链表作用所在
hash 相同的对象hash肯定相同,不同的对象hash也有可能相同,学名"碰撞"
因此,根据key进行存储时候,根据key算出hash再计算出下标的时候有可能会冲突啊,万一冲突了咋整呢?
开发者想出了将相同的下标的元素用链表连成一个串儿,这样找的时候直接找到对应的index位置,然后从链表里挨个儿的去读取就行。

数据结构ok了,接下来聊下参数,其实主要是数据结构,后面不懂也没关系,但是为了高逼格,大家懂得,再学个散列桶+加载因子吧,看名字就很高大上,其实作用两句话就欧了。且看我装逼:

这里写图片描述

你创建一个数组好歹得有个长度吧,平时我们new HashMap() 创建出来的数组有个默认长度,这个长度就是16 也就是第一个圈圈

数组总不能无限大吧,所以咯第二个圈圈就是最大长度

这个数组是自动扩充的,啥时候扩充呢,得有个标准吧,对不对,不可能满了之后去扩充,肯定不行,16*0.75=12 如果是16初始长度到12的时候自动扩充一倍到32 第三个圈圈就是加载因子,是不是很屌很高大上,其实没啥牛逼的,名字很唬人

那么问题来了,大伙儿会说:老板我还想装逼,这点逼数不够啊
不够满分来凑啊,那咱们就来点更装逼的

HashMap的长度为什么是2的N次方?
这个问题哪里来的,下面上图

这里写图片描述

大家看的没错,这张图是hashmap的构造方法,讲述的如何创建一个hashmap对象,圈圈里的就是上面所说的容量跟加载因子
第三个圈圈是重点,大家看到的没错,<<位移运算,位移运算是基于二进制的,理所当然的结果是2的N次方咯,哈哈哈,这就是问题的由来,输入的容量参数最终进行的是位移运算,并不是你输入的值作为最终容量大小。
比方说你输入5实际容量是2的5次方也就是32哈哈哈,然后就是为啥要这么搞咧?
继续上图演示
这里写图片描述

hashmap第二个核心点也是精髓点来了,就一句话或者更简单的来说还是一个&逻辑运算符
这段代码的功能干嘛的呢,获取数组的下标,根据你输入的key计算hash然后调用这个方法计算出在数组中对应的位置

前面已经聊过,hashmap这玩意儿呢虽然是用数组,但是尼玛还是无序存储杂乱无章的,随机算出hash进行存储,位置无序,因此空间上利用度不是很高,为了追求最大化的利用率,使用2的N次方能达到最高效率的利用率

为毛?

1.减小哈希冲突概率 【人家总结的很好我就画蛇添足了】
避免数组小标越界

假如当前Entry数组长度为len,插入节点时,需要对key的hashcode进行二次哈希,然后跟len-1相与(得到的值一定小于len,避免数组越界)
如果len是2的N次方,那么len-1的后N位二进制一定是全1

假设有两个key,他们的hashcode不同,分别为code1和code2
code1和code2分别与一个后N位全1的二进制相与,结果一定也不同
但是,如果code1和code2分别与一个后N位非全1的二进制相与,结果有可能相同

也就是说,如果len是2^N,不同hashcode的key计算出来的数组下标一定不同;
否则,不同hashcode的key计算出来的数组下标一定相同。

所以HashMap长度为全1,可以减小哈希冲突概率。

2.提高计算下标的效率

如果len的二进制后n位非全1,与len-1相与时,0与1相与需要取反。
如果len的二进制后n位全1,完全不需要取反。

如果len为2^N,那么与len-1相与,跟取余len等价,而与运算效率高于取余hash%len。
如果len不是2^N,与len-1相与,跟取余len不等价。
效果跟取余一样

小结下:很多文章都介绍说要想数组的空间利用率最大化hash%lenth最好,如果len为2^N的时候会等价,为毛呢?
这个问题问的好啊
len为2^N的时候呢,len-1换算为二进制是111111…
跟hash&运算的时候 若果hash的位数超级长,那么len-1的头部会自动设置为0,这样就把hash多余的位数给砍掉了,保留了跟len-1等长的数据,效果跟取余一样.
懂了没,嘎嘎嘎

参考文献:
https://blog.csdn.net/ghsau/article/details/16890151
https://blog.csdn.net/crazyboy12138/article/details/80420620
https://blog.csdn.net/zx582727090/article/details/51099190
https://blog.csdn.net/qq_27093465/article/details/54669308

hashMap 的key value 可以为null,但是hashtable不允许
hashmap的key还可以是对象,如果是对象类型,那么需要你去重写hashcode以及equals方法,以避免hash碰撞

参考文献
https://www.cnblogs.com/williamjie/p/9358291.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值