HashMap的扩容原理

 新增过程:
      初始化长度为16数组,数组的形式为Node<k,v> tab[]
     JDk1.8 数组+链表+红黑树

 什么情况下会发生扩容:(新容量 = 旧容量<<1)
a.当前同一个索引下链表长度大于8且小于64扩容
 b.当数组的格子占有率达到了0.75的时候会进行扩容

HashMap的put方法的具体流程?

源代码:

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}
 
 /**
 * Implements Map.put and related methods.
 * 实现了map的put和相关方法
 * @param hash  key的hash值(key的hash高16位+高16位与低16位的异或运算)
 * @param key 键
 * @param value 值  
 * @param onlyIfAbsent onlyIfAbsent为true的时候不要修改已经存在的值,如果onlyIfAbsent为false,当插入的元素已经在HashMap中已经拥有了与其key值和hash值相同的元素,仍然需要把新插入的value值覆盖到旧value上。如果nlyIfAbsent为true,则不需要修改
 * @param evict evict如果为false表示构造函数调用
 * @return 返回旧的value值(在数组桶或链表或红黑树中找到存在与插入元素key值和hash值相等的元素,就返回这个旧元素的value值),如果没有发现相同key和hash的元素则返回null
 */
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
    // tab用来临时存放数组table引用   p用来临时存放数组table桶中的bin
    // n存放HashMap容量大小   i存放当前put进HashMap的元素在数组中的位置下标
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    // table未初始化或者长度为0,进行扩容
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    // (n - 1) & hash 确定元素存放在哪个桶中,桶为空,新生成结点放入桶中(此时,这个结点是放在数组中)
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    // 桶中已经存在元素
    else {
        // e记录当前节点  k记录key值
        Node<K,V> e; K k;
        // 比较桶中第一个元素(数组中的结点)的hash值相等,key相等
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
                // 将第一个元素赋值给e,用e来记录。直接将插入的新元素覆盖旧元素
                e = p;
        // hash值不相等,即key不相等并且该节点为红黑树结点,将元素插入红黑树
        else if (p instanceof TreeNode)
            // 放入树中
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        // 为链表结点
        else {
            // 在链表最末插入结点(尾插法)
            for (int binCount = 0; ; ++binCount) {
                // 到达链表的尾部
                if ((e = p.next) == null) {
                    // 在尾部插入新结点
                    p.next = newNode(hash, key, value, null);
                    // 结点数量达到阈值(默认为 8 ),执行 treeifyBin 方法
                    // 这个treeifyBin()方法会根据 HashMap 数组情况来决定是否转换为红黑树。
                    // 只有当数组长度大于或者等于 64 的情况下,才会执行转换红黑树操作,以减少执行效率。否则,就是只是对数组扩容。
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
					// 树化操作
                        treeifyBin(tab, hash);
                    // 跳出循环  此时e=null,表示没有在链表中找到与插入元素key和hash值相同的节点
                    break;
                }
                // 判断链表中结点的key值和Hash值与插入的元素的key值和Hash值是否相等
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    // 若相等,则不用将其插入了,直接跳出循环
                    break;
                // 用于遍历桶中的链表,与前面的e = p.next组合,可以遍历链表
                p = e;
            }
        }
        // 当e!=null时,表示在数组桶或链表或红黑树中存在key值、hash值与插入元素相等的结点。此时就直接用原有的节点就可以了,不用插入新的元素了。此时e就代表原本就存在于HashMap中的元素
        if (e != null) {
            // 记录e的value,也就是旧value值
            V oldValue = e.value;
            // onlyIfAbsent为false或者旧值为null,则需要用新的value值对旧value值进行覆盖
            if (!onlyIfAbsent || oldValue == null)
                //用新值替换旧值
                e.value = value;
            // 替换旧值时会调用的方法(默认实现为空)
            afterNodeAccess(e);
            // 返回旧值
            return oldValue;
        }
    }
    // 结构性修改,记录HashMap被修改的次数,主要用于多线程并发时候
    ++modCount;
    // 实际大小大于阈值则扩容    ++size只有在插入新元素才会执行,如果发现HashMap中已经存在了相同key和hash的元素,就不会插入新的元素,在上面就已经执行return了,也就不会改变size大小
    if (++size > threshold)
        resize();
    // 插入成功时会调用的方法(默认实现为空)
    afterNodeInsertion(evict);
    // 没有找到原有相同key和hash的元素,则直接返回Null
    return null;
}
总结:

put:
//        第一次新增创建长度为16的数组
//        ①:计算地址(n - 1) & hash,当前如果为null,
//              直接放元素  newNode()
//        ②:如果当前不为null,哈希冲突,判断值key是否相同,
//            Key相同则覆盖重写
//        ③: 当前的值不为null,哈希冲突,且key结果不同,
//        先判断是否为树,如果为树,用树的方式创建新节点,加载到树上
//        如果不为树,则为链表形式,找到p.next=null位置,放我的新元素
//            链表长度大于8且数组小于64扩容,扩容数组,数组*2
//        当链表长度大于8且数组大于64时,将链表转红黑树

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值