HashMap实现内存扩容

HashMap

首先,什么是HashMap?百度百科说基于哈希表的 Map 接口的实现。什么是Map呢?可以简单理解为<key, value>。

Action

HashMap实现的方式有多种,主要区别个人认为就是在Hash查找,如何解决Hash碰撞上。(Hash函数用什么,怎么哈希,这个跟着大家调用库就好了,反正我也证明不了Hash函数里面的数学问题)

在这里说的是 数组和链表混用的一种,如下图(以下图片都来自参考文章)。
在这里插入图片描述
该为JDK7的 HashMap设计模型,横向为数组结构,纵向为链表结构。
然后很容易理解,当一个key哈希之后,没有产生碰撞,直接放进数组的格子里,当key哈希之后产生了碰撞,即纵向扩展,用链表的串联起来。

这里有一个小问题?

不同的key1, key2产生相同哈希值hash_value,串联之后,取值时,输入的key也会哈希之后得到相同的hash_value去定位,此时如何区分该取key1还是该取key2呢?
此时只需要把key1,key2本身也存在链表的节点里,通过hash_value定位到链表头结点,然后遍历链表,对比key和key1,key2,相同的那个就是要取出的值。

那怎么通过hash_value定位呢?

既然是数组,那就遍历数组,用数组元素的值和hash_value对比即可。此时的时间复杂度为O(n).如果数组的长度很长,O(n)就会很大,可以将数组转为红黑树来避免O(n)。

HashMap扩容

golang的slice有len,cap两个参数,cap为容量,当当前长度大于cap时,cap会乘2,有个测试的小例子

同理HashMap也是作了类似的处理,
HashMap关键参数

size
	hashmap中的kv组合的总数量,拿上图举例,size = 4(数组元素)+4(链表节点) = 9。
capacity
	容量,hashmap中数组的长度,也称作桶的数量,默认值是DEFAULT_INITIAL_CAPACITY=16。拿上图举例,capacity=10。
loadFactor
	装载因子,默认是0.75,此数值可以衡量hashmap满的程度。
threshold
	扩容阀指,threshold = capacity * loadFactor ,当hashmap的size大于或者等于 threshold 时,hashmap将进行扩容。
MAXIMUM_CAPACITY
	HashMap的最大容量,1 << 30 = 230

但HashMap在设计上会把数组的长度设计为2的N次幂,实现方式为只要初始化数组长度时长度为2的n次幂,之后每次数组扩容时乘2,那数组就一直为2的n次幂,这样的设计的目的是减少哈希碰撞,使得要存储的元素能均匀的分布在数组中,如何避免的,在数学上,在下暂时还不能理解。

如何在初始化数组长度时长度为2的n次幂

有一段代码一定要备注一下,个人觉得很巧妙。

private static int roundUpToPowerOf2(int number) {
        // assert number >= 0 : "number must be non-negative";
        //number非负数
        //并且最大是2的30次幂
        return number >= MAXIMUM_CAPACITY
                ? MAXIMUM_CAPACITY
                : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
    }

参考文章:

  1. https://www.cnblogs.com/goalone/p/9459318.html
发布了21 篇原创文章 · 获赞 15 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/Tangs_/article/details/98497587