hashmap是散列表吗_图文剖析HashMap源码,就是这么简单!

4264e6ee10f5831f21e092a7c9075f63.png

作者 |  Java3y

来源 |  Java3y

f1a6e3f14e789278e3764f8db8a6d094.gif

一、HashMap剖析

首先看看HashMap的顶部注释说了些什么:

3062a9c5be9faa15855a5a687c08cc86.png

再来看看HashMap的类继承图:

b1267bb6c7139184edbc909a554c746c.png

下面我们来看一下HashMap的属性:

acb0507abee67a5bc5351e4a30e0b411.png

成员属性有这么几个:

6c39804216d39ce3448eebf990962fd1.png

再来看一下hashMap的一个内部类Node:

a17355f818d51a3926b1800a553bf25f.png

我们知道Hash的底层是散列表,而在Java中散列表的实现是通过数组+链表的~

再来简单看看put方法就可以印证我们的说法了:数组+链表-->散列表

4e0d18fafb51984077531d51b504e94d.png

我们可以简单总结出HashMap:

  • 无序,允许为null,非同步

  • 底层由散列表(哈希表)实现

  • 初始容量和装载因子对HashMap影响挺大的,设置小了不好,设置大了也不好

1.1HashMap构造方法

HashMap的构造方法有4个:

ec75cd05df9a1ed3ac96b38e1bd19715.png
f4ead84925cedd8a653660ff43f8390a.png

在上面的构造方法最后一行,我们会发现调用了tableSizeFor(),我们进去看看:

2633df8f1709fccddd41b8376c660bdd.png

这是位运算算法,具体流程可参考:

  • https://www.cnblogs.com/loading4/p/6239441.html

  • https://blog.csdn.net/fan2012huan/article/details/51097331

看完上面可能会感到奇怪的是:为啥是将2的整数幂的数赋给threshold

  • threshold这个成员变量是阈值,决定了是否要将散列表再散列。它的值应该是:capacity * load factor才对的。

其实这里仅仅是一个初始化,当创建哈希表的时候,它会重新赋值的:

e34d7724e7599044962eb73f9e3555d2.png

至于别的构造方法都差不多,这里我就不细讲了:

99214ea0c738e0ab21f06c12e0694a75.png

1.2put方法

put方法可以说是HashMap的核心,我们来看看:

464f28b21ee5a55a1d28bd1ec2f65f23.png

我们来看看它是怎么计算哈希值的:

0ef8caaa18f7969f8fb984356905c1e1.png

为什么要这样干呢??我们一般来说直接将key作为哈希值不就好了吗,做异或运算是干嘛用的??

我们看下来:

1a7e4353f30d62c6ae10e48e2d3e706f.png

我们是根据key的哈希值来保存在散列表中的,我们表默认的初始容量是16,要放到散列表中,就是0-15的位置上。也就是tab[i = (n - 1) & hash]。可以发现的是:在做&运算的时候,仅仅是后4位有效~那如果我们key的哈希值高位变化很大,低位变化很小。直接拿过去做&运算,这就会导致计算出来的Hash值相同的很多。

而设计者将key的哈希值的高位也做了运算(与高16位做异或运算,使得在做&运算时,此时的低位实际上是高位与低位的结合),这就增加了随机性,减少了碰撞冲突的可能性!

下面我们再来看看流程是怎么样的:

a5f4046ee0fa66b7dffe7931b744df90.png

新值覆盖旧值,返回旧值测试:

0a3dff4a4f41bbd60ef696deccaf0d6f.png

接下来我们看看resize()方法,在初始化的时候要调用这个方法,当散列表元素大于capacity * load factor的时候也是调用resize()

1429ed721b6fa314c01f8e1a4b8bad47.png

1.3get方法

afe74ea610c800206e7f5e82cc1746b1.png

接下来我们看看getNode()是怎么实现的:

eadc8d6c501e4fc24c94f990df7615fa.png

1.4remove方法

60b8eba2907014d362c28fa985011c7a.png

再来看看removeNode()的实现:

8caa962b2f987cfe4c30b8d013232de9.png

二、HashMap与Hashtable对比

从存储结构和实现来讲基本上都是相同的。它和HashMap的最大的不同是它是线程安全的,另外它不允许key和value为null。Hashtable是个过时的集合类,不建议在新代码中使用,不需要线程安全的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换

6f39656b1efddffc1e136fcf8efd1dd3.png

Hashtable具体阅读源码可参考:

  • https://blog.csdn.net/panweiwei1994/article/details/77427010

  • https://blog.csdn.net/panweiwei1994/article/details/77428710

四、总结

在JDK8中HashMap的底层是:数组+链表(散列表)+红黑树

在散列表中有装载因子这么一个属性,当装载因子*初始容量小于散列表元素时,该散列表会再散列,扩容2倍!

装载因子的默认值是0.75,无论是初始大了还是初始小了对我们HashMap的性能都不好

  • 装载因子初始值大了,可以减少散列表再散列(扩容的次数),但同时会导致散列冲突的可能性变大(散列冲突也是耗性能的一个操作,要得操作链表(红黑树)

  • 装载因子初始值小了,可以减小散列冲突的可能性,但同时扩容的次数可能就会变多!

初始容量的默认值是16,它也一样,无论初始大了还是小了,对我们的HashMap都是有影响的:

  • 初始容量过大,那么遍历时我们的速度就会受影响~

  • 初始容量过小,散列表再散列(扩容的次数)可能就变得多,扩容也是一件非常耗费性能的一件事~

从源码上我们可以发现:HashMap并不是直接拿key的哈希值来用的,它会将key的哈希值的高16位进行异或操作,使得我们将元素放入哈希表的时候增加了一定的随机性

还要值得注意的是:并不是桶子上有8位元素的时候它就能变成红黑树,它得同时满足我们的散列表容量大于64才行的~

6349bf08e6398e046095152be0367faa.png
85de1a296ab5645b1e36fe37dc45f78e.png
参考资料:
  • 《Core Java》

  • https://blog.csdn.net/panweiwei1994/article/details/76555359

  • https://zhuanlan.zhihu.com/p/28216267

  • https://blog.csdn.net/fan2012huan/article/details/51097331

  • https://www.cnblogs.com/chinajava/p/5808416.html


ps:文字版的学不过瘾?往下看,小慕来给大家送福利啦,超值实战,仅需一元哦,没有套路~一块钱让你get到源码大师的编程内功和编程思想!

/ 推荐一门超值HashMap源码课程 /

39a0d43c2e8114027f97d997337ce054.png 无套路,仅需一元直接购买! f9e61a78ae099eccd00208cbfacc2379.png 6c750e6ade64e47b79c50a1be7d18ca2.png

END

了解课程详情,请点击原文阅读~


e79b90eb62f2dfd1021a2a645c85c561.gif

● 盘点那些改变世界的代码!

● 程序员一般通过什么途径接私活?

● 我偷窥了XX个程序员的微信群聊,简直不忍直视····

● 用好这42款Chrome插件,每年轻松给你省出一个年假!

675a99d7e2ccc5e97d2fee62bd40e046.png

002c3671bba361a5e9851e0e86ad0bd8.gif

Tips:

# 点下“在看”❤️

# 然后,留个言踩个楼?每周都有不定时福利哦4608e115d9c7014d4044ce8cf2486d10.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值