HashMap的初始容量机制及扩容机制

  通常在我们的应用中,HashMap是用到最多的数据结构之一,在JDK1.8之前,它的底层结构是数组+链表,而在JDK1.8之后,为了查询效率的优化(主要是当哈希碰撞较多的时候),它的底层结构变成了数组+链表+红黑树。今天就来探讨一下HashMap的扩容机制,这也是面试时被问到最多的问题。首先看一下源码中HashMap的四种构造方法。

  从源码中可以看出:HashMap提供四种构造方法:一是给定初始容量和加载因子的构造方法,二是给定初始容量,使用默认的加载因子,三是什么参数都不给,使用默认的初始容量和默认的加载因子,四是传进一个Map,使用默认的加载因子。从上面的构造方法可以看出,无论是使用默认的初始容量,还是使用默认的初始容量,当你调用HashMap的构造方法时,HashMap是没有进行初始化容量,也就是现在是一个空的HashMap(容量为0),这是因为HashMap使用的懒加载机制,只有你第一次向HashMap中添加元素时,才进行第一次的容量设置,查看put(K,V)的源码:

从上图中可以看出,put(K key, V value)调用了putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict)的方法,在putVal的方法中,当第一次向HashMap中添加对象时,会进行一个判空的处理,这时就调用resize()方法对HashMap进行容量设置,此时会有两种情况的根据容量初始化。

第一种情况:当我们没有设置初始化容量时,HashMap就使用默认的初始化容量,也就是16.

第二种情况:当我们设置了初始化容量,HashMap就会按照我们设置的容量进行设置吗?答案是不一定。当你设置的初始化容量是2的n次方时,就会按照你设置的容量设置;当你设置的初始化容量不是2的n次方时,就会按照大于你设置的那个值但是最接近你设置的那个值的2的n次方进行设置。听起来比较拗口,下面从源码和实例进行说明。

  从HashMap的第一个构造方法我们可以看出,当我们给定初始容量时,会调用tableSizeFor(initialCapacity)方法进行容量设置,

这一系列的无符号右移操作和按位或运算返回的什么结果,我们可以通过一个测试来说明。测试类如下:

运算结果如下:

结合源码和实例,就可以体会到HashMap给定了初始容量时,它本身处理这个值得机制了。

还有就是HashMap什么时候进行扩容呢?我们回到put()方法的源码中,发现有下面一段代码:

其中size是HashMap中添加键值对的数量,而threshold是容量*加载因子(capacity * load factor)得出的值,也是就当我们的容量是16,加载因子是0.75时,当键值对的数量大于16*0.75=12时,HashMap会进行扩容操作,回到resize()方法中,看它是怎么扩容的?

可以看出,在容量不大于最大值的情况下,HashMap是以2倍的容量进行扩容的。那么问题又来了,HashMap为什么会是2倍的形式进行扩容呢?下次再来分享。

发布了78 篇原创文章 · 获赞 0 · 访问量 5125

猜你喜欢

转载自blog.csdn.net/qq_44813090/article/details/104338210