HashMap怎么学(二)

上一篇HashMap怎么学(一)我们通过一个简单的例子解释了HashMap的内部结构。

这篇文章我们会在上一篇的例子基础上改进下,给大家解释HashMap的内部数组是怎么扩展的,以及其内部是如何生成链表的。

代码案例

public static void part2_1() {
		// 步骤一,初始化代码,插入部分12条数据
		Map<Integer, Integer> map = new HashMap<>(20);
		for (int i = 0; i < 12; i++) {
			map.put(i, i);
		}

		// 步骤二,插入第13条数据,触发内部数组长度扩展
		map.put(12, 12);

		// 步骤三,插入特定hash值,触发hash碰撞,生成链表
		for (int i = 1; i < 5; i++) {
			map.put(32 * i, 32 * i);
		}
}
复制代码

执行完上述代码后HashMap的内部结构变化如图:

HashMap内部结构图2-1

代码解释

  • 步骤一执行完后,HashMap内部会生成一个长度为16的数组,数组的前12位的Node对象的key-value值和数组index值相同(HashMap内key-value键值对数量为12)。
  • 步骤二执行完后,HashMap的内部数组长度变成原来的2倍(32),并且第12位的Node对象的key-value值也为12。这里涉及到HashMap内部数组的扩张机制:

DEFAULT_LOAD_FACTOR默认值是0.75(也可以通过HashMap构造函数传递),表示当HashMap内key-value键值对数量超过内部数组长度0.75时,对内部数组进行扩张。

执行完步骤一后,内部数组长度为16,当key-value键值对数量超过16×0.75=12时内部数组会进行扩张,下面为HashMap部分源码。

    /**
     * The load factor used when none specified in constructor.
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
复制代码

内部数组长度扩展的长度是原值左移一位,也就是乘以2(newCap = oldCap << 1),下面为HashMap部分源码。

final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
  ......
  Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
  ......
复制代码
  • 步骤三执行完后,原内部数组index为0的Node对象变成了一个链表的头结点。

HashMap是通过tab[i = (n - 1) & hash]来定位新的key-value键值对要插入到内部数组的哪个位置的,这时n为内部数组长度32,n-1对应到二进制为00011111,Integer的hashcode值就是它本身的int值,这时hashcode就发生冲突了,都插入到了index为0的位置,组成了一个链表:

00011111&00000000=0

00011111&00100000(32)=0

00011111&01000000(64)=0

00011111&10000000(128)=0

下面为HashMap部分源码:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
        ........
          for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
         .......
复制代码

这篇文章就分享到这里,后续还有HashMap其他文章会陆续发布。

大家如果有问题欢迎在评论区留言一起探讨,同时也可以关注收藏“小西学JAVA”和同名公众号,有文章更新会及时收到通知。

Demo代码位置

gitee.com/xiaoxijava/…

猜你喜欢

转载自juejin.im/post/7034714080178864136