史上最详细的HashMap源码解析,小白必看

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;                 // 数组长度和索引

    // 如果哈希表数组为空或长度为0,则进行扩容操作,并将新的数组赋值给tab变量
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;

    // 计算要插入的节点在数组中的索引位置i,并获取该位置处的节点p
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e;  // 用于记录相同键值的已存在节点
        K k;          // 用于存储节点的键值

        // 如果头结点p的哈希值和键值与要插入的哈希值和键值相等,则将头结点作为待插入节点
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;

        // 如果头结点p是树节点,则调用树节点的putTreeVal方法,在树中执行插入操作
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            // 如果头结点p不是树节点,则遍历链表,查找是否已存在相同键值的节点
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    // 如果链表遍历到末尾仍未找到相同键值的节点,则将新节点加入链表末尾
                    p.next = newNode(hash, key, value, null);
                    // 如果链表长度达到树化阈值,则将链表转化为红黑树
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1是为了排除第一个节点
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }

        // 如果找到了相同键值的节点e,则更新该节点的值
        if (e != null) { // 键值对已存在
            V oldValue = e.value;
            // 如果onlyIfAbsent标志为false,或者旧值oldValue为null,则进行更新
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e); // 对节点进行访问后的处理
            return oldValue;    // 返回旧值
        }
    }

    ++modCount;  // 修改次数+1
    if (++size > threshold) // 如果元素数量超过了阈值,则进行数组扩容操作
        resize();
    afterNodeInsertion(evict); // 插入节点后的处理
    return null; // 返回null,表示插入操作完成
}
         
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; // 扩容阈值翻倍
    } else if (oldThr > 0) // 如果旧数组为空但扩容阈值不为0
        newCap = oldThr; // 将扩容阈值作为新数组的容量
    else { // 旧数组为空且扩容阈值为0,表示使用默认值
        newCap = DEFAULT_INITIAL_CAPACITY; // 设置默认的初始容量
        newThr = (int) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); // 计算默认的扩容阈值
    }
    if (newThr == 0) { // 如果新的扩容阈值为0
        float ft = (float) newCap * loadFactor; // 计算阈值的浮点数表示
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float) MAXIMUM_CAPACITY ?
                (int) ft : Integer.MAX_VALUE); // 根据容量和负载因子计算新的扩容阈值
    }
    threshold = newThr; // 更新扩容阈值
    @SuppressWarnings({ "rawtypes", "unchecked" })
    Node<K, V>[] newTab = (Node<K, V>[]) new Node[newCap]; // 创建新的数组
    table = newTab; // 更新table引用指向新的数组
    if (oldTab != null) { // 如果旧数组不为空
        for (int j = 0; j < oldCap; ++j) { // 遍历旧数组
            Node<K, V> e;
            if ((e = oldTab[j]) != null) { // 如果旧数组位置上有节点
                oldTab[j] = null; // 清空旧数组该位置的节点引用
                if (e.next == null) // 如果该节点是单个节点
                    newTab[e.hash & (newCap - 1)] = e; // 直接放入新数组对应的位置
                else if (e instanceof TreeNode) // 如果该节点是红黑树节点
                    ((TreeNode<K, V>) e).split(this, newTab, j, oldCap); // 执行红黑树的分割操作
                else { // 如果该Node<K, V> loHead = null, loTail = null; // 低位链表的头节点和尾节点
                    Node<K, V> hiHead = null, hiTail = null; // 高位链表的头节点和尾节点
                    Node<K, V> next;
                    do {
                        next = e.next;
                        if ((e.hash & oldCap) == 0) { // 根据节点的hash值判断是低位还是高位链表
                            if (loTail == null)
                                loHead = e; // 初始化低位链表的头节点
                            else
                                loTail.next = e; // 将当前节点链接到低位链表尾部
                            loTail = e; // 更新低位链表尾节点
                        } else {
                            if (hiTail == null)
                                hiHead = e; // 初始化高位链表的头节点
                            else
                                hiTail.next = e; // 将当前节点链接到高位链表尾部
                            hiTail = e; // 更新高位链表尾节点
                        }
                    } while ((e = next) != null); // 遍历链表,直到所有节点处理完毕
                    if (loTail != null) { // 如果存在低位链表
                        loTail.next = null; // 将低位链表尾节点的next指针置为null
                        newTab[j] = loHead; // 将低位链表头节点放入新数组对应位置
                    }
                    if (hiTail != null) { // 如果存在高位链表
                        hiTail.next = null; // 将高位链表尾节点的next指针置为null
                        newTab[j + oldCap] = hiHead; // 将高位链表头节点放入新数组对应位置
                    }
                }
            }
        }
    }
    return newTab; // 返回新的数组
}
                   


final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
                               int h, K k, V v) {
    Class<?> kc = null; // 用于存储键的类类型
    boolean searched = false; // 是否已经进行过查找
    TreeNode<K,V> root = (parent != null) ? root() : this; // 确定根节点
    for (TreeNode<K,V> p = root;;) { // 循环遍历树节点
        int dir, ph; K pk;
        if ((ph = p.hash) > h) // 当前节点哈希值大于要插入节点的哈希值
            dir = -1; // 需要向左子树查找
        else if (ph < h) // 当前节点哈希值小于要插入节点的哈希值
            dir = 1; // 需要向右子树查找
        else if ((pk = p.key) == k || (k != null && k.equals(pk))) // 节点的键与要插入节点的键相等
            return p; // 返回当前节点
        else if ((kc == null &&
                  (kc = comparableClassFor(k)) == null) ||
                 (dir = compareComparables(kc, k, pk)) == 0) {
            // 执行到这里表示要插入的键与当前节点的键不能直接比较大小,需要进行额外的比较
            if (!searched) { // 如果尚未进行过查找
                TreeNode<K,V> q, ch;
                searched = true; // 设置已进行查找标记
                if (((ch = p.left) != null &&
                     (q = ch.find(h, k, kc)) != null) || // 在左子树中查找
                    ((ch = p.right) != null &&
                     (q = ch.find(h, k, kc)) != null)) // 在右子树中查找
                    return q; // 返回查找到的节点
            }
            // 执行到这里表示在子树中没有找到与要插入节点相同的键,需要通过比较确定插入的方向
            dir = tieBreakOrder(k, pk);
        }

        TreeNode<K,V> xp = p; // 记录父节点
        if ((p = (dir <= 0) ? p.left : p.right) == null) { // 到达插入位置
            Node<K,V> xpn = xp.next; // 父节点的后继节点
            TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn); // 创建新的树节点
            if (dir <= 0)
                xp.left = x; // 插入到父节点的左子树
            else
                xp.right = x; // 插入到父节点的右子树
            xp.next = x; // 更新父节点的后继节点
            x.parent = x.prev = xp; // 设置新节点的父节点和前驱节点
            if (xpn != null)
                ((TreeNode<K,V>)xpn).prev = x; // 更新后继节点
            moveRootToFront(tab, balanceInsertion(root, x)); // 调整树结构以保持平衡
            return null; // 返回null表示插入成功
        }
    }
}
final void treeifyBin(Node<K,V>[] tab, int hash) {
    int n, index; // 定义变量n、index
    Node<K,V> e; // 定义变量e

    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
        resize(); // 如果tab为空或者长度小于最小树化容量,则调用resize()方法进行扩容

    else if ((e = tab[index = (n - 1) & hash]) != null) {
        // 如果tab不为空且对应索引位置上的节点e不为null
        TreeNode<K,V> hd = null, tl = null; // 定义变量hd和tl,初始值为null

        do {
            // 创建替换节点p作为e节点的替代节点,第二个参数为null
            TreeNode<K,V> p = replacementTreeNode(e, null);

            if (tl == null)
                hd = p; // 如果tl为null,将p赋值给hd作为头节点
            else {
                p.prev = tl; // 否则,将p的prev引用设置为tl
                tl.next = p; // 将tl的next引用设置为p
            }

            tl = p; // 更新tl为p

        } while ((e = e.next) != null); // 循环直到遍历完e的所有后继节点

        if ((tab[index] = hd) != null)
            hd.treeify(tab); // 将头节点hd作为索引位置的新节点,并调用treeify()方法将链表转化为树结构
    }
}

final void split(HashMap<K, V> map, Node<K, V>[] tab, int index, int bit) {
        TreeNode<K, V> b = this; // 当前节点作为树节点
        // 用于保存拆分后的低位链表的头和尾节点
        TreeNode<K, V> loHead = null, loTail = null;
        // 用于保存拆分后的高位链表的头和尾节点
        TreeNode<K, V> hiHead = null, hiTail = null;
        int lc = 0, hc = 0; // 用于记录低位链表和高位链表的节点数量

        // 遍历当前树节点链表
        for (TreeNode<K, V> e = b, next; e != null; e = next) {
            next = (TreeNode<K, V>) e.next;
            e.next = null; // 断开当前节点与下一个节点的连接

            // 根据节点的hash值判断应该加入低位链表还是高位链表
            if ((e.hash & bit) == 0) {
                if ((e.prev = loTail) == null)
                    loHead = e; // 如果低位链表尾节点为null,当前节点作为低位链表的头节点
                else
                    loTail.next = e; // 否则,将当前节点链接到低位链表尾节点的next引用上
                loTail = e; // 更新低位链表尾节点为当前节点
                ++lc; // 低位链表节点数量加1
            } else {
                if ((e.prev = hiTail) == null)
                    hiHead = e; // 如果高位链表尾节点为null,当前节点作为高位链表的头节点
                else
                    hiTail.next = e; // 否则,将当前节点链接到高位链表尾节点的next引用上
                hiTail = e; // 更新高位链表尾节点为当前节点
                ++hc; // 高位链表节点数量加1
            }
        }

        // 处理低位链表
        if (loHead != null) {
            if (lc <= UNTREEIFY_THRESHOLD)
                tab[index] = loHead.untreeify(map); // 如果低位链表节点数量小于等于阈值,将其转化为普通节点并更新到tab对应的位置上
            else {
                tab[index] = loHead; // 否则,直接将低位链表的头节点设置到tab对应的位置上
                if (hiHead != null)
                    loHead.treeify(tab); // 如果同时存在高位链表,将低位链表转化为树结构
            }
        }
// 处理高位链表
        if (hiHead != null) {
            if (hc <= UNTREEIFY_THRESHOLD)
                tab[index + bit] = hiHead.untreeify(map); // 如果高位链表节点数量小于等于阈值,将其转化为普通节点并更新到tab对应的位置上
            else {
                tab[index + bit] = hiHead; // 否则,直接将高位链表的头节点设置到tab对应的位置上
                if (loHead != null)
                    hiHead.treeify(tab); // 如果同时存在低位链表,将高位链表转化为树结构
            }
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值