HashMap自我见解
1、概述
想要API的链接:最新版API JDK11版本中文释义
我们可以看到API中说明了,HashMap是基于哈希表的Map接口的实现。那其实java中对于HashMap的实现是采用对象数组+链表,而且在链表达到一定长度时,链表会转变成二叉树存储。
那么我们首先去学习什么是哈希表。
这里要穿插一个点,我们在提到哈希表时,我们要知道有一个方法是Object.hashCode()。那我为什么提到它呢,请往下看。
好,那我们接着打开API查看这个方法,首先搜索Object,下面有这个方法。
解释一下,这其中所谓的支持此方法,是你所编写的类重写了hasCode()方法,而散列表就是哈希表。
2、数据结构:哈希表
那我们来讲一讲哈希表:
那我觉得我上面的描述已经很完整了,不需要我再进行一步步的分析了。当然为了广大志同道合的好友难呢过狗更清晰的明白这张图的内容,我会再次去描述一下。
首先,上面已经说过,我们也了解一下Object.hashCode()这个方法了。那么我们因为要举例的嘛,所以我创建一个Object对象,别问我为什么创建Object,这还不是因为java万物皆对象。那之后我们搞一个哈希表,我们默认长度为16。那图中也有描述,在存储到哈希表时,会调用我们的hashCode值,然后与哈希表数组长度进行取模运算。那我们想一下,我们不管是多大的数据,取模16,得到的数字一定是0 - 15,对吧。那我们得到的这些0 - 15的数据就是下标。而下标可以确定对象存在哪个位置。
我们图中也举了个例子:一个数据的hash值为17,那我们的这个对象会存储到1下标。那么首先呢,我们要进行对这个1下标的存储数据进行判断,判断是否有数据,只有这个1下标指向的位置为null时,那我们可以直接存储进去。那如果有数据怎么办?哎,我就想存到这个1下标,有数据我也要存。这就好比一个茅坑一只屁股,那你非要和别人一起,那怎么办?放心,有办法,这就体现出我们的M哈希表的好处来了。
我们之前说过哈希表这个数据结构是什么样的?是对象数组+链表的,也就是说,当我们这个下标指向的位置有数据了,那又来了一个下标为1的数据,我们就将其排在这个数据之后,依次排开。
那又有一个情况:那在JDK1.8之后呢,当我们的哈希桶(能放一个链表的这个小框)中的数据数据大于8时,我们的链表会转换为红黑二叉树。(那如果有想要看的,可以去网上找,当然如果想要,可以直接留言,我会关注,指不定就更一个)。好,闲言少叙,废话不说啊,继续正文。
当然要注意一点就是,它不是在减到8的时候又转换成链表的,它是到了6才从红黑二叉树转换成了链表。
面试题
这里可以说个小面试题:就是如图中所言,当HR提问:哎,小火鸡,我问你一个问题啊,如果是有一个哈希桶中的已知数据为7,注意听,那我要往6减,问一定会从红黑树转成链表吗?
别已学过这,就兴致勃勃的回答,哎,是的呢!是个屁啊,首先从逻辑都知道,那人家会问你一个傻瓜问题吗?会,但是也要当成不会,认真回答,仔细思考,沉思半刻,让HR觉得,哎这个小虎吉不错奥。你看他对待问题的态度多认真啊!真好啊,就录取他了。那这时其实你已经成功了一半,另一半就是你要准确全面的回答这个问题,按照您刚刚的描述,我觉得您问的不够准确,您只说了已知数据为7,但是您并没有确定现在的存储方式是链表还是红黑二叉树,那如果是链表的话,就说明次数据就已没有到8,所以不用转换,而如果是红黑二叉树的话,那就在减到6的时候转换成链表结构。其实说到这里,HR就已经在心里对你暗暗点头,要录用你了,如果大家真的遇到这个问题一定要这样回答,录用了也给我报一个喜讯啊!
----------------------------------------------------华丽的分割线-----------------------------------------------------------------
哈希表的实现
好了说到这里,我们就结合API和源码来去看一下java中HashMap对于哈希表的实现。
首先来看我们的API:
我们之前说过了,HashMap有一个初始的容量,以及默认的加载因子(0.75),我们java中的哈希表使用的是对象数组+链表,根据哈希值计算下标,然后确定存在哪一个下标中,当然,我们的对象在存储的过程中会产生冲突,因为他们存储的下标是相同的,也就是这个下标冲突,哈希值冲突有很大概率会发生,那在java中就采用了链表与红黑树的方式来处理这个问题。好之前的内容就复述到这里,接下来我们来看一看,我们这个构造方法中所出现的初始容量和默认加载因子在一个位置构建的。我们来看源码。
我们首先来看一下他的这个构造方法:
这里我就不再赘述了,好吧,图上都要注释。
我们来看一下它的put方法。
通过这个找:
我们来看一下它通过put方法去添加数据的流程。
我们可以看到这个方法是有多么的简单,就是传了一个键一个值,然后就是先计算哈希值。然后呢,把这个哈希值和键和值传给了一个叫putVal的方法去存储。
好,我们点开这个putVal方法。
那我们可以看到,这个方法的实现就很有趣啊,我决定采用在源码上注释的方式为大家简单的说一下这个方法的实现。
/**put 数据获取方法
我们可以看到,这里用到了很多牛逼的语法,使得代码看起来很简洁,但是也难以阅读,
那接下来就由我来给大家分析分析部分源码的实现流程是怎样的。
如果觉得我说的没问题还可以,很不错的话,请给个三连(点赞,收藏,关注我),谢谢!啊,我又在痴人说梦了,但也许屏幕前的大帅比和大漂亮就给我三连也说不定啊!!!
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,