数据结构-HashMap源码【基础篇】


HashMap 和 HashSet 是 java 中利用哈希表实现的 Map 和 Set。在JDK8之前,HashMap就是数组+链表;在JDK8之后,HashMap就是数组 + 链表 + 红黑树。即就是当某个链表过长时,不是整表扩容,而是选择将对应的链表树化变为红黑树。接下来简单分析一下JDK8的HashMap源码。

类的属性

在这里插入图片描述

某个子链表的长度>=8且整个哈希表的元素个数>=64才会将链表树化。
若子链表的长度>=8但是整个哈希表的元素个数<64,此时进行整表扩容。

构造方法

在无参构造中,只是初始化了负载因子的大小,保存元素的Node数组没有初始化。
在这里插入图片描述
在有两个参数的有参构造中,判断了初始化容量和负载因子是否合法。判断完之后,负载因子是直接赋值的,但是初始化容量并不是直接赋值的。而是通过一个函数继续改变初始化容量的值。
在这里插入图片描述
在这里插入图片描述
所以不管传入多大的初始化容量,当调用内部的有参构造时,传入任意长度的初始化容量,HashMap最终会返回一个最接近该参数的2^n的值作为数组长度。 比如传入15,返回的值是16。到这里,真正存储元素的Node数组仍然没初始化。

put方法

HashMap中,最核心的一些逻辑都在它的put方法中。从源码中可以看到,当调用内部的putVal方法时,先要对当前传入的key值做哈希运算,得到一个整型数字,进一步就可以知道当前的键值在哪个哈希桶存储。
在这里插入图片描述

hash函数

在这里插入图片描述
当传入的key值为空时,哈希函数的结果就是0;当key不为空时,先通过所有位数计算了一个哈希值,将一个32为的数值右移16位得到一个高16位的数值,将这两种数值异或。这样高低16位参与异或运算,既保留了原有数值的所有信息,也尽可能的得到分散的数值,最终使得不同的数值尽可能的保存在不同的哈希桶中,这样的效率是最高的。
在这里插入图片描述
Object类提供的hashCode方法:任意对象都有此方法,调用该方法,默认会将当前对象的内存地址做一个哈希运算,得到一个32位的整型数字。

问题:解释Object类的hashCode与equals的关系和区别。
区别:
equals:判断两个对象的内容是否相同。
hashCode:默认的hashCode将对象地址映射为整型,需要将任意数据类型转为整型。
关系:
两个对象hashCode返回相同的数值,equals的结果不一定是true,因为两个不同的key值是有可能发生哈希碰撞使得哈希映射结果相同,但key是不相同的;两个对象的equals方法返回true,hashCode 一定会返回相同的数值。两个对象key值相同,通过哈希函数得到的结果一定相同。
若需要用到hashCode方法,需要对默认的Object进行重写,使用对象的属性来计算hashCode。因为通过对象的地址算hashCode没有意义,理论上所有对象的地址都不相同。

putVal方法

在这里插入图片描述
首先来看,第一个if分支。在创建hashMap对象时,Table一直没有初始化过,当第一次调用put方法时,table为null。此时第一个if分支的作用就是在初始化Node数组。当第一次调用put方法时,对Node数组进行初始化操作。这个操作称为 “懒加载”,产生集合对象时,并不初始化内部空间,第一次向该集合添加元素时,才初始化内部空间的大小。第一个if分支调用了resize方法,这个方法身兼两职:初始化+扩容,即默认初始化长度为16的数组,或者扩容为原数组的一倍。
在这里插入图片描述
再看一下putVal方法的第二个if分支,其中(n -1) & hash在做什么操作?
在这里插入图片描述
其实就是当n = 2^N时,(n -1) & k 就相当于k%n,这样做的好处是用位运算替代取模运算,进一步加快计算索引的速度。这就解释了为什么在hashMap中,数组的长度必须是2^N,此时就可以使用位运算来取代取模运算,加快计算速度。所以(n -1) & hash 相当于hash%n,将一个很大的值映射到一个小的集合中,这就是当前数组的索引下标。所以,第二个分支就是当前key和value是新元素, 而且它的子链表一个元素都没有,此时作为头节点插入到哈希桶中。
接着看putVal方法。

在这里插入图片描述

在这个大的else分支中, 此时哈希表已经初始化,对应的链表也不为空,若子链表中包含了指定的key值,只需要更新value即可;否则相当于插入一对新的元素,要么是红黑树的插入,要么是链表的插入,最终,插入新元素之后,size ++,如果大于threshold,调用resize()扩容即可。

以上是对JDK8的hashMap源码的一些个人的简单理解,在源码中最主要的还是其中的hash函数和putVal方法。后续将继续深入挖掘hashMap源码。

继续加油努力!!!
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值