• HashMap 源码分析


    HashMap

    HashMap 能解决什么问题?什么时候使用 HashMap?

    1)HashMap 是基于哈希表的 Map 接口实现,允许使用 null 键和 null 值。
    2)假定哈希函数将元素均匀地分布在各个桶中,HashMap 的 get 和 put 方法提供稳定的性能。
    3)影响 HashMap 性能的两个参数:初始容量和加载因子,当元素个数超出初始容量和加载因子乘积时,
    将进行扩容,哈希表将具有大约两倍的桶数。默认的初始容量为 16,默认的加载因子为 0.75,当需要添加大量元素时,
    提供合适的初始容量和加载因子可以避免频繁的 rehash 操作以提高性能。
    4)HashMap 不是线程安全的,HashMap 视图方法返回的迭代器都是快速失败的,面对并发修改,HashMap 将抛出 ConcurrentModificationException 异常。
    

    如何使用 HashMap?

    1)需要保存对象间映射关系时,可以使用 HashMap。
    2)当明确知道元素个数范围时,可以指定一个初始化容量来减少扩容次数。
    3)降低 HashMap 的加载因子可以减少 hash 碰撞,通过减少指定键的比较次数来提升读写性能,但是会造成更多内存的浪费。
    

    使用 HashMap 有什么风险?

    1)HashMap 桶的数量为 2 的幂,存在一定的内存浪费。
    2)多线程环境下使用 HashMap,在扩容时可能导致死循环。
    

    HashMap 核心操作的实现原理?

    • 创建实例
        /**
         * 默认的初始 Bucket 数量,必须是 2 的幂
         */
        static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    
        /**
         * 哈希表最大的 Bucket 数量
         * MUST be a power of two <= 1<<30.
         */
        static final int MAXIMUM_CAPACITY = 1 << 30;
    
        /**
         * 未在构造函数中指定时使用的默认加载因子
         */
        static final float DEFAULT_LOAD_FACTOR = 0.75f;
    
        /**
         * 单向链表转换为红黑树时的链表长度
         */
        static final int TREEIFY_THRESHOLD = 8;
    
        /**
         * 红黑树转换为单向链表时的元素个数
         */
        static final int UNTREEIFY_THRESHOLD = 6;
    
        /**
         * 单向链表转换为红黑树时,最小的 Bucket 数量。
         */
        static final int MIN_TREEIFY_CAPACITY = 64;
        
        /**
         * 创建一个初始容量为 16、加载因子为 0.75 的空 HashMap 实例
         */
        public HashMap() {
            this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
        }
    
        /**
         * 创建一个初始容量为 initialCapacity、加载因子为 0.75 的空 HashMap 实例
         */
        public HashMap(int initialCapacity) {
            this(initialCapacity, DEFAULT_LOAD_FACTOR);
        }
    
        /**
         * 创建一个初始容量为 initialCapacity、加载因子为 loadFactor 的空 HashMap 实例
         */
        public HashMap(int initialCapacity, float loadFactor) {
            if (initialCapacity < 0) {
                throw new IllegalArgumentException("Illegal initial capacity: " +
                        initialCapacity);
            }
            // HashMap 的最大 Bucket 数量为 1 << 30
            if (initialCapacity > MAXIMUM_CAPACITY) {
                initialCapacity = MAXIMUM_CAPACITY;
            }
            if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
                throw new IllegalArgumentException("Illegal load factor: " +
                        loadFactor);
            }
            this.loadFactor = loadFactor;
            this.threshold = HashMap.tableSizeFor(initialCapacity);
        }
    
        /**
         * 基于目标容量计算最接近的 2 的幂
         */
        static final int tableSizeFor(int cap) {
            int n = cap - 1;
            n |= n >>> 1;
            n |= n >>> 2;
            n |= n >>> 4;
            n |= n >>> 8;
            n |= n >>> 16;
            return n < 0 ? 1 : n >= MAXIMUM_CAPACITY ? MAXIMUM_CAPACITY : n + 1;
        }
    
    • 添加元素
        /**
         * 底层存储 Bucket 的节点数组,在第一次使用时完成初始化,并按需自动扩容,
         * 数组长度必须是 2 的幂。
         */
        transient Node<K,V>[] table;
    
        /**
         * 往 HashMap 中添加新的元素,如果目标键已经存在,则旧值被替换为新值,并返回旧值。
         */
        @Override
        public V put(K key, V value) {
            return putVal(HashMap.hash(key), key, value, false, true);
        }
    
        /**
         * 计算键的哈希值
         */
        static final int hash(Object key) {
            int h;
            // HashMap 允许使用 null 键
            return key == null ? 0 : (h = key.hashCode()) ^ h >>> 16;
        }
    
        /**
         * 基础的哈希表节点
         */
        static class Node<K,V> implements Map.Entry<K,V> {
            /**
             * 键的哈希值
             */
            final int hash;
            /**
             * 键
             */
            final K key;
            /**
             * 值
             */
            V value;
            /**
             * 单向链表的下一个节点
             */
            Node<K,V> next;
        }
    
        /**
         * @param hash 键的哈希值
         * @param key 键
         * @param value 值
         * @param onlyIfAbsent 为 true 时,不会覆盖已经存在的值
         * @param evict 为 false 时,哈希表处于创建模式
         * @return previous value, or null if none
         */
        final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                boolean evict) {
            Node<K,V>[] tab; Node<K,V> p; int n, i;
            // 1)第一次添加元素时会进入该分支,
            if ((tab = table) == null || (n = tab.length) == 0) {
                // 读取 table 的长度
                n = (tab = resize()).length;
            }
            // 通过【table.length-1 & 键的哈希值】定位 bucket 的位置,如果该位置为空,则创建新的节点完成添加。
            if ((p = tab[i = n - 1 & hash]) == null) {
                // 1)创建一个普通节点
                tab[i] = newNode(hash, key, value, null);
            } else {
                // 2)哈希表的键发生碰撞,目标 bucket 已经有元素存在
                Node<K,V> e; K k;
                // 2-1)bucket 头元素和新增元素的哈希值相等并且键值也相等
                if (p.hash == hash &&
                        ((k = p.key) == key || key != null && key.equals(k))) {
                    e = p;
                    // 2-3)当前 bucket 已经是一颗红黑树
                } else if (p instanceof TreeNode) {
                    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                } else {
                    // 2-2)遍历单向链表完成元素添加
                    for (int binCount = 0; ; ++binCount) {
                        // 当前节点的后置节点为空
                        if ((e = p.next) == null) {
                            // 创建一个新的节点,加入到当前节点后面
                            p.next = newNode(hash, key, value, null);
                            // 如果单向链表的元素个数大于等于 9 个
                            if (binCount >= TREEIFY_THRESHOLD - 1) {
                                // 单向链表红黑树化
                                treeifyBin(tab, hash);
                            }
                            break;
                        }
                        // 如果当前节点的哈希值和新增元素的哈希值相等,并且键值也相等
                        if (e.hash == hash &&
                                ((k = e.key) == key || key != null && key.equals(k))) {
                            // 元素已经找到,则退出循环
                            break;
                        }
                        // 更新迭代节点为单向链表的下一个节点
                        p = e;
                    }
                }
                // 目标节点已经存在
                if (e != null) { // existing mapping for key
                    // 读取旧值
                    final V oldValue = e.value;
                    // 如果允许覆盖或原来的值为 null
                    if (!onlyIfAbsent || oldValue == null) {
                        // 写入新值
                        e.value = value;
                    }
                    // 被 LinkedHashMap 使用的钩子函数
                    afterNodeAccess(e);
                    // 返回旧值
                    return oldValue;
                }
            }
            // 增加并发修改计数值
            ++modCount;
            // 如果元素个数超出阈值,则进行扩容
            if (++size > threshold) {
                resize();
            }
            // 被 LinkedHashMap 使用的钩子函数
            afterNodeInsertion(evict);
            return null;
        }
    
        /**
         * 初始化或将 table 扩容为原来的两倍,下标为 index 的 bucket 中的元素只能迁移到新 table 中下标为 index
         * 处或 index+oldCapacity 处。
         */
        final Node<K,V>[] resize() {
            // 暂存旧的 table
            final Node<K,V>[] oldTab = table;
            // 读取旧的哈希表容量
            final int oldCap = oldTab == null ? 0 : oldTab.length;
            // 读取旧的扩容阈值
            final int oldThr = threshold;
            int newCap, newThr = 0;
            // 3)执行第二次扩容
            if (oldCap > 0) {
                // 旧容量已经达到最大值
                if (oldCap >= MAXIMUM_CAPACITY) {
                    // 阈值设置为 Integer.MAX_VALUE
                    threshold = Integer.MAX_VALUE;
                    return oldTab;
                }
                // 旧容量扩大一倍也小于最大容量值,并且旧容量大于等于初始化容量 16
                else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                        oldCap >= DEFAULT_INITIAL_CAPACITY)
                {
                    // 阈值扩大一倍
                    newThr = oldThr << 1; // double threshold
                }
            }
            // 2)基于带参数构造函数创建 HashMap 实例时,第一次扩容会进入该分支
            else if (oldThr > 0) {
                newCap = oldThr;
            } else {               // zero initial threshold signifies using defaults
                // 1)基于无参构造函数创建的哈希表第一次扩容时,设置初始容量为 16,扩容阈值为 12.
                newCap = DEFAULT_INITIAL_CAPACITY;
                newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
            }
            if (newThr == 0) {
                final float ft = newCap * loadFactor;
                newThr = newCap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY ?
                        (int)ft : Integer.MAX_VALUE;
            }
            // 写入新的阈值
            threshold = newThr;
            @SuppressWarnings({"rawtypes","unchecked"})
            // 创建新的 table
            final Node<K,V>[] newTab = new Node[newCap];
            // 写入新的 table
            table = newTab;
            // 非第一次扩容
            if (oldTab != null) {
                // 循环遍历每个 bucket
                for (int j = 0; j < oldCap; ++j) {
                    Node<K,V> e;
                    // 读取 bucket 首节点
                    if ((e = oldTab[j]) != null) {
                        // 首节点不为 null,需要执行数据迁移
                        oldTab[j] = null;
                        // 1)bucket 中只有一个元素
                        if (e.next == null) {
                            // 直接将其赋值到新 table 的指定 bucket 中即可
                            newTab[e.hash & newCap - 1] = e;
                            // 2)旧的 bucket 已经是一颗红黑树,则需要执行拆分
                        } else if (e instanceof TreeNode) {
                            ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                        } else { // preserve order
                            /**
                             * 3)旧的 bucket 是一个单向链表
                             * loHead 表示 lowHead,低位的头节点
                             * loTail 表示 lowTail,低位的尾节点
                             * hiHead 表示 highHead,高位的头节点
                             * hiTail 表示 highTail,高位的为节点
                             */
                            Node<K,V> loHead = null, loTail = null;
                            Node<K,V> hiHead = null, hiTail = null;
                            Node<K,V> next;
                            do {
                                // 读取第二个节点
                                next = e.next;
                                // 1)节点哈希值和旧容量相与为 0,则将该节点放在低位 bucket 中
                                if ((e.hash & oldCap) == 0) {
                                    // 如果是低位链表的第一个节点
                                    if (loTail == null) {
                                        // 设置头节点为 e
                                        loHead = e;
                                    } else {
                                        // 将当前节点加入链表尾部
                                        loTail.next = e;
                                    }
                                    // 更新低位链表尾节点为当前迭代节点
                                    loTail = e;
                                }
                                // 2)该节点需要放置在高位 bucket 中
                                else {
                                    // 如果是高位链表的第一个节点
                                    if (hiTail == null) {
                                        // 设置头节点为 e
                                        hiHead = e;
                                    } else {
                                        // 将当前节点加入链表尾部
                                        hiTail.next = e;
                                    }
                                    // 更新高位链表尾节点为当前迭代节点
                                    hiTail = e;
                                }
                                // 迭代下一个节点
                            } while ((e = next) != null);
                            // 低位链表尾节点不为 null
                            if (loTail != null) {
                                // 尾节点的后置节点置为空
                                loTail.next = null;
                                // 将头结点设置为低位 bucket 的首节点
                                newTab[j] = loHead;
                            }
                            // 高位链表尾节点不为 null
                            if (hiTail != null) {
                                // 尾节点的后置节点置为空
                                hiTail.next = null;
                                // 将头结点设置为高位 bucket 的首节点
                                newTab[j + oldCap] = hiHead;
                            }
                        }
                    }
                }
            }
            return newTab;
        }
    
        // 创建一个普通节点
        Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
            return new Node<>(hash, key, value, next);
        }
    
        /**
         * 尝试进行单向链表的红黑树化
         */
        final void treeifyBin(Node<K,V>[] tab, int hash) {
            int n, index; Node<K,V> e;
            // 1)如果 table 的长度小于 64
            if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) {
                // 则执行扩容操作
                resize();
                // 2)获取到单向链表的头节点,并暂存在 e 中
            } else if ((e = tab[index = n - 1 & hash]) != null) {
                // hd 表示头节点 head,tl 表示尾节点 tail
                TreeNode<K,V> hd = null, tl = null;
                do {
                    // 将当前节点替换为树节点
                    final TreeNode<K,V> p = replacementTreeNode(e, null);
                    // 如果是第一个节点
                    if (tl == null) {
                        // 头节点设置为当前节点
                        hd = p;
                    } else {
                        // 当前节点的前置节点设置为尾节点
                        p.prev = tl;
                        // 尾节点的后置节点设置为当前节点
                        tl.next = p;
                    }
                    // 更新尾节点为当前节点
                    tl = p;
                    // 顺序遍历单向链表的所有节点,红黑树本身也维护着节点的顺序
                } while ((e = e.next) != null);
                // 将头结点作为 bucket 的第一个节点
                if ((tab[index] = hd) != null) {
                    // 执行红黑树化操作
                    hd.treeify(tab);
                }
            }
        }
    
        // For treeifyBin
        TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
            return new TreeNode<>(p.hash, p.key, p.value, next);
        }
    
            /**
             * 执行单向链表的红黑树化过程
             */
            void treeify(Node<K,V>[] tab) {
                TreeNode<K,V> root = null;
                for (TreeNode<K,V> x = this, next; x != null; x = next) {
                    // 暂存当前节点的后置节点
                    next = (TreeNode<K,V>)x.next;
                    // 节点的左右子节点置为 null
                    x.left = x.right = null;
                    // 1)还不存在根节点,则说明当前节点是第一个节点
                    if (root == null) {
                        // 根节点的父节点为 null
                        x.parent = null;
                        // 根节点为黑色
                        x.red = false;
                        root = x;
                    }
                    // 2)根节点已经存在
                    else {
                        // 读取节点的键
                        final K k = x.key;
                        // 读取节点的哈希值
                        final int h = x.hash;
                        // 键比较器
                        Class<?> kc = null;
                        for (TreeNode<K,V> p = root;;) {
                            /**
                             * dir 表示 directory,-1 表示左子树,+1 表示右子树
                             * ph 表示 parent hash,父节点的哈希值
                             * pk 表示 parent key,父节点的键
                             */
                            int dir, ph;
                            // 读取父节点的键
                            final K pk = p.key;
                            // 父节点的哈希值比当前节点大,则当前节点位于左子树
                            if ((ph = p.hash) > h) {
                                dir = -1;
                                // 父节点的哈希值比当前节点小,则当前节点位于右子树
                            } else if (ph < h) {
                                dir = 1;
                            } else if (kc == null &&
                                    (kc = HashMap.comparableClassFor(k)) == null ||
                                    (dir = HashMap.compareComparables(kc, k, pk)) == 0) {
                                dir = TreeNode.tieBreakOrder(k, pk);
                            }
                            // 暂存当前节点的父节点
                            final TreeNode<K,V> xp = p;
                            // 父节点的左子节点或右子节点为空
                            if ((p = dir <= 0 ? p.left : p.right) == null) {
                                // 更新当前节点的父节点
                                x.parent = xp;
                                // 新节点位于父节点的左侧
                                if (dir <= 0) {
                                    xp.left = x;
                                    // 新节点位于父节点的右侧
                                } else {
                                    xp.right = x;
                                }
                                // 平衡插入
                                root = TreeNode.balanceInsertion(root, x);
                                break;
                            }
                        }
                    }
                }
                // 确保给定的 root 节点是红黑树的第一个节点
                TreeNode.moveRootToFront(tab, root);
            }
    
        /**
         * 如果 X 实现了 Comparable 接口,则返回 X 的运行时类型,否则返回 null
         */
        static Class<?> comparableClassFor(Object x) {
            if (x instanceof Comparable) {
                Class<?> c; Type[] ts, as; ParameterizedType p;
                // 字符串类型优先处理
                if ((c = x.getClass()) == String.class) {
                    return c;
                }
                // 目标对象的运行时类型 c 直接实现的接口列表
                if ((ts = c.getGenericInterfaces()) != null) {
                    for (final Type t : ts) {
                        // t 是一个泛型接口
                        if (t instanceof ParameterizedType &&
                                // 获取泛型接口的接口类型,并且 t 是 Comparable 接口
                                (p = (ParameterizedType) t).getRawType() ==
                                Comparable.class &&
                                // 获取泛型接口的实际参数类型数组
                                (as = p.getActualTypeArguments()) != null &&
                                // 实际参数类型数组长度为 1,即只有一个泛型参数,并且其实际类型就是对象的类型
                                as.length == 1 && as[0] == c) {
                            // 返回对象类型
                            return c;
                        }
                    }
                }
            }
            return null;
        }
    
        /**
         * Returns k.compareTo(x) if x matches kc (k's screened comparable
         * class), else 0.
         */
        @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable
        static int compareComparables(Class<?> kc, Object k, Object x) {
            // k 和 x 的运行时类型不一致,则返回 0,否则返回比较结果
            return x == null || x.getClass() != kc ? 0 :
                ((Comparable)k).compareTo(x);
        }
    
            static int tieBreakOrder(Object a, Object b) {
                int d;
                if (a == null || b == null ||
                        // 如果 a 和 b 的运行时类型不一致,则比较他们的类全名
                        (d = a.getClass().getName().
                        compareTo(b.getClass().getName())) == 0) {
                    // a 和 b 的运行时类型一致,一般会返回 -1
                    d = System.identityHashCode(a) <= System.identityHashCode(b) ?
                            -1 : 1;
                }
                return d;
            }
    
            static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                    TreeNode<K,V> x) {
                // 新插入节点的颜色为红色
                x.red = true;
                /**
                 * xp 表示 x parent,新插入节点的父节点
                 * xpp 表示 x parent parent,新插入节点的祖父节点
                 * xppl 表示 x parent parent left,新插入节点的祖父节点的左节点
                 * xppr 表示 x parent parent right,新插入节点的祖父节点的右节点
                 */
                for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
                    // 新插入节点就是根节点本身
                    if ((xp = x.parent) == null) {
                        // 根节点是黑色的
                        x.red = false;
                        // 返回根节点
                        return x;
                    }
                    /**
                     * 1)x 节点的父节点为黑色,则红色节点可以直接插入,直接返回 root 无需变换
                     * 2)x 的父节点为红色,并且父节点就是 root?【因为根节点必须是黑色的,直接插入红色节点无需变换】
                     */
                    else if (!xp.red || (xpp = xp.parent) == null) {
                        return root;
                    }
                    /**
                     * 进入此分支
                     * 1)父节点是红色的,当前节点也是红色的,遇到两个相连的红色节点时,需要进行旋转变换。
                     * 2)x 的祖父节点不为 null。
                     */
                    // x 的父节点是祖父节点的左孩子
                    if (xp == (xppl = xpp.left)) {
                        /**
                         *        xpp
                         *       /  
                         *  [红]xp   xppr[红]
                         *     / 
                         * 此种情况下只需要执行颜色变换
                         */
                        if ((xppr = xpp.right) != null && xppr.red) {
                            // 祖父节点的左右孩子都设置为 黑色
                            xppr.red = false;
                            xp.red = false;
                            // 祖父节点设置为红色
                            xpp.red = true;
                            // 暂存祖父节点
                            x = xpp;
                        }
                        else {
                            /**
                             *        xpp
                             *       /
                             *  [红]xp
                             *     / 
                             *        x[红]
                             * 此种情况下需要执行左旋
                             */
                            if (x == xp.right) {
                                // x 暂存为父节点
                                root = TreeNode.rotateLeft(root, x = xp);
                                // 暂存祖父节点
                                xpp = (xp = x.parent) == null ? null : xp.parent;
                            }
                            /**
                             *      xpp
                             *      /
                             *   [红]xp
                             *    /
                             * [红]x
                             * 原本是这种结构,或是由于执行了左旋变成这种结构,则需要进行一次右旋
                             */
                            if (xp != null) {
                                // 设置父节点颜色为黑色
                                xp.red = false;
                                // 如果祖父节点不为 null
                                if (xpp != null) {
                                    // 设置祖父节点颜色为红色
                                    xpp.red = true;
                                    // 执行右旋
                                    root = TreeNode.rotateRight(root, xpp);
                                }
                            }
                        }
                    }
                    // x 的父节点是祖父节点的右孩子
                    else {
                        /**
                         *          xpp
                         *          /
                         *   [红]xppl xp[红]
                         * 此种情况下只需要进行颜色变换
                         */
                        if (xppl != null && xppl.red) {
                            // 祖父节点的左子节点设置为黑色
                            xppl.red = false;
                            // 祖父节点的右子节点也设置为黑色
                            xp.red = false;
                            // 祖父节点设置为红色
                            xpp.red = true;
                            // 暂存祖父节点,当前子树已经是红黑树结构
                            x = xpp;
                        }
                        else {
                            /**  xpp
                             *    
                             *     xp[红色]
                             *    /
                             * [红]x
                             * 此种情况下需要执行右旋
                             */
                            if (x == xp.left) {
                                root = TreeNode.rotateRight(root, x = xp);
                                xpp = (xp = x.parent) == null ? null : xp.parent;
                            }
                            /** xpp
                             *   
                             *    xp[红]
                             *     
                             *      x[红]
                             * 原本是这种结构,或右旋之后变成这种结构,则需要执行左旋
                             */
                            if (xp != null) {
                                // 父节点设置为黑色
                                xp.red = false;
                                if (xpp != null) {
                                    // 祖父节点设置为红色
                                    xpp.red = true;
                                    root = TreeNode.rotateLeft(root, xpp);
                                }
                            }
                        }
                    }
                }
            }
    
            // 红黑树左旋
            static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                    TreeNode<K,V> p) {
                /**
                 * r 表示 right 右节点
                 * pp 表示 parent parent 祖父节点
                 * rl 表示 right left 右节点的左节点
                 * http://www.cnblogs.com/finite/p/8251587.html
                 */
                TreeNode<K,V> r, pp, rl;
                if (p != null && (r = p.right) != null) {
                    if ((rl = p.right = r.left) != null) {
                        rl.parent = p;
                    }
                    if ((pp = r.parent = p.parent) == null) {
                        (root = r).red = false;
                    } else if (pp.left == p) {
                        pp.left = r;
                    } else {
                        pp.right = r;
                    }
                    r.left = p;
                    p.parent = r;
                }
                return root;
            }
            
            // 红黑树右旋
            static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
                    TreeNode<K,V> p) {
                /**
                 * l 表示 left 左节点
                 * pp 表示 parent parent 祖父节点
                 * lr 表示 left right 左节点的右节点
                 * http://www.cnblogs.com/finite/p/8251587.html
                 */
                TreeNode<K,V> l, pp, lr;
                if (p != null && (l = p.left) != null) {
                    if ((lr = p.left = l.right) != null) {
                        lr.parent = p;
                    }
                    if ((pp = l.parent = p.parent) == null) {
                        (root = l).red = false;
                    } else if (pp.right == p) {
                        pp.right = l;
                    } else {
                        pp.left = l;
                    }
                    l.right = p;
                    p.parent = l;
                }
                return root;
            }
    
            static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
                int n;
                if (root != null && tab != null && (n = tab.length) > 0) {
                    // 读取 root 节点所在的 bucket 下标
                    final int index = n - 1 & root.hash;
                    // 读取 bucket 的首节点
                    final TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
                    // root 不是首节点,则需要执行变换
                    if (root != first) {
                        /**
                         * rn 表示 root next
                         * rp 表示 root previous
                         */
                        Node<K,V> rn;
                        // 将 root 置为首节点
                        tab[index] = root;
                        // 读取 root 的前置节点
                        final TreeNode<K,V> rp = root.prev;
                        // 读取 root 的后置节点
                        if ((rn = root.next) != null) {
                            ((TreeNode<K,V>)rn).prev = rp;
                        }
                        // root 的前置节点不为 null
                        if (rp != null) {
                            rp.next = rn;
                        }
                        // 首节点不为 null,则将其设置为 root 的前置节点
                        if (first != null) {
                            first.prev = root;
                        }
                        // root 的后置节点设置为首节点
                        root.next = first;
                        // root 的前置节点置为 null
                        root.prev = null;
                    }
                    assert TreeNode.checkInvariants(root);
                }
            }
    
            /**
             * Tree version of putVal.
             */
            TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
                    int h, K k, V v) {
                // 键比较器
                Class<?> kc = null;
                //
                boolean searched = false;
                // 读取根节点
                final TreeNode<K,V> root = parent != null ? root() : this;
                for (TreeNode<K,V> p = root;;) {
                    /**
                     * dir 表示 directory,-1 表示左子树,+1 表示右子树
                     * ph 表示 parent hash,父节点的哈希值
                     * pk 表示 parent key,父节点的键
                     */
                    int dir, ph; K pk;
                    // 1)查找节点或新增节点位于左子树
                    if ((ph = p.hash) > h) {
                        dir = -1;
                        // 2)查找节点或新增节点位于右子树
                    } else if (ph < h) {
                        dir = 1;
                        // 3)节点已经存在,则直接返回
                    } else if ((pk = p.key) == k || k != null && k.equals(pk)) {
                        return p;
                        // 4)哈希值相等,但是键不相等【指定类型的多个子类对象的哈希值相等】
                    } else if (kc == null &&
                            (kc = HashMap.comparableClassFor(k)) == null ||
                            (dir = HashMap.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 = TreeNode.tieBreakOrder(k, pk);
                    }
    
                    // 父节点更新为当前节点
                    final TreeNode<K,V> xp = p;
                    // 当前节点的左孩子或右孩子为空,表示可以直接插入
                    if ((p = dir <= 0 ? p.left : p.right) == null) {
                        // 读取后置节点
                        final Node<K,V> xpn = xp.next;
                        // 创建新的树节点,并维持单向链表结构
                        final 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;
                        }
                        // 平衡红黑树并移动根节点
                        TreeNode.moveRootToFront(tab, TreeNode.balanceInsertion(root, x));
                        return null;
                    }
                }
            }
    
    • 如果不存在,则添加
        @Override
        public V putIfAbsent(K key, V value) {
            return putVal(HashMap.hash(key), key, value, true, true);
        }
    
    • 根据键读取值
        /**
         * 根据键读取值,如果键不存在,则返回 null,否则返回映射的值
         */
        @Override
        public V get(Object key) {
            Node<K,V> e;
            return (e = getNode(HashMap.hash(key), key)) == null ? null : e.value;
        }
    
        final Node<K,V> getNode(int hash, Object key) {
            /**
             * tab:table
             * e:element
             * n:length
             * k:key
             */
            Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
            // 根据目标哈希定位到 bucket 不为 null
            if ((tab = table) != null && (n = tab.length) > 0 &&
                    (first = tab[n - 1 & hash]) != null) {
                // 1)首节点就是查找的目标节点,则直接返回
                if (first.hash == hash && // always check first node
                        ((k = first.key) == key || key != null && key.equals(k))) {
                    return first;
                }
                // 2)首节点不是目标节点,并且存在后置节点
                if ((e = first.next) != null) {
                    // 2-1)如果首节点是红黑树节点
                    if (first instanceof TreeNode) {
                        // 则进行树节点查找
                        return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                    }
                    // 2-2)循环遍历单向链表查找目标节点
                    do {
                        if (e.hash == hash &&
                                ((k = e.key) == key || key != null && key.equals(k))) {
                            // 找到则直接返回
                            return e;
                        }
                    } while ((e = e.next) != null);
                }
            }
            // 未找到,则防护 null
            return null;
        }
    
            TreeNode<K,V> getTreeNode(int h, Object k) {
                return (parent != null ? root() : this).find(h, k, null);
            }
    
            /**
             * 从根节点开始查找指定键
             */
            TreeNode<K,V> find(int h, Object k, Class<?> kc) {
                TreeNode<K,V> p = this;
                do {
                    /**
                     * ph:parent hash
                     * dir:directory
                     * pk:parent key
                     * pl:parent left
                     * pr:parent right
                     */
                    int ph, dir; K pk;
                    final TreeNode<K,V> pl = p.left, pr = p.right;
                    TreeNode<K,V> q;
                    if ((ph = p.hash) > h) {
                        // 1)parent 哈希值大于目标哈希,更新 parent 为左子节点
                        p = pl;
                    } else if (ph < h) {
                        // 2)parent 哈希值小于目标哈希,更新 parent 为右子节点
                        p = pr;
                    } else if ((pk = p.key) == k || k != null && k.equals(pk)) {
                        // 3)查找到目标节点,则直接返回
                        return p;
                    } else if (pl == null) {
                        // 4)哈希值相等但是键不相等,且左子节点为 null,则更新 parent 为右子节点
                        p = pr;
                    } else if (pr == null) {
                        // 5)哈希值相等但是键不相等,且右子节点为 null,则更新 parent 为左子节点
                        p = pl;
                        // 6)哈希值相等但是键不相等,并且左右子树都不为空。
                    } else if ((kc != null ||
                            (kc = HashMap.comparableClassFor(k)) != null) &&
                            (dir = HashMap.compareComparables(kc, k, pk)) != 0) {
                        // 更新 parent
                        p = dir < 0 ? pl : pr;
                        // 7)基于键比较器比较是相等的,尝试从右子树查找目标元素
                    } else if ((q = pr.find(h, k, kc)) != null) {
                        // 找到则直接返回
                        return q;
                    } else {
                        // 8)目标元素不存在右子树中,则更新 parent 为左子节点
                        p = pl;
                    }
                // 只要 parent 不为 null, 则迭代遍历 
                } while (p != null);
                // 目标键不存在,则返回 null
                return null;
            }
    
    • 如果键不存在,则返回默认值
        @Override
        public V getOrDefault(Object key, V defaultValue) {
            Node<K,V> e;
            return (e = getNode(HashMap.hash(key), key)) == null ? defaultValue : e.value;
        }
    
    • 如果目标键存在,则进行值替换,并返回旧值,否则返回 null,注意旧值也可能是 null
        @Override
        public V replace(K key, V value) {
            Node<K,V> e;
            if ((e = getNode(HashMap.hash(key), key)) != null) {
                final V oldValue = e.value;
                e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
            return null;
        }
    
    • 如果存在指定的键值对,则将目标值替换为新值,替换成功返回 true,类似与 CAS 操作
        @Override
        public boolean replace(K key, V oldValue, V newValue) {
            Node<K,V> e; V v;
            if ((e = getNode(HashMap.hash(key), key)) != null &&
                    ((v = e.value) == oldValue || v != null && v.equals(oldValue))) {
                e.value = newValue;
                afterNodeAccess(e);
                return true;
            }
            return false;
        }
    
    • 替换 HashMap 中的所有元素
        @Override
        public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
            Node<K,V>[] tab;
            if (function == null) {
                throw new NullPointerException();
            }
            // 如果 HashMap 不为空,则进行逐个元素替换
            if (size > 0 && (tab = table) != null) {
                final int mc = modCount;
                for (Node<K,V> e : tab) {
                    for (; e != null; e = e.next) {
                        // 通过函数式接口根基键和值计算新值
                        e.value = function.apply(e.key, e.value);
                    }
                }
                if (modCount != mc) {
                    throw new ConcurrentModificationException();
                }
            }
        }
    
    • 清空 HashMap
        /**
         * 移除 HashMap 中所有的键值对
         */
        @Override
        public void clear() {
            Node<K,V>[] tab;
            modCount++;
            if ((tab = table) != null && size > 0) {
                // 重置计数值
                size = 0;
                // 将 bucket 的所有首节点置为 null
                for (int i = 0; i < tab.length; ++i) {
                    tab[i] = null;
                }
            }
        }
    
    • 返回键值对的总数
        /**
         * 返回键值对的总数
         */
        @Override
        public int size() {
            return size;
        }
    
    • HashMap 是否为空
        /**
         * HashMap 是否为空
         */
        @Override
        public boolean isEmpty() {
            return size == 0;
        }
    
    • 是否包含指定的键
        @Override
        public boolean containsKey(Object key) {
            return getNode(HashMap.hash(key), key) != null;
        }
    
    • 是否包含指定的值
        /**
         * 是否包含指定的值
         */
        @Override
        public boolean containsValue(Object value) {
            Node<K,V>[] tab; V v;
            if ((tab = table) != null && size > 0) {
                for (Node<K,V> e : tab) {
                    for (; e != null; e = e.next) {
                        if ((v = e.value) == value ||
                                value != null && value.equals(v)) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }
    
    • 合并或删除操作
        /**
         * 1)如果指定的键值对不存在,则尝试新增。
         * 2)如果存在,则使用函数值接口基于旧值和新值计算合并后的目标值
         * 3)如果合并值为 null,则删除该节点。
         */
        @Override
        public V merge(K key, V value,
                BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
            if (value == null) {
                throw new NullPointerException();
            }
            if (remappingFunction == null) {
                throw new NullPointerException();
            }
            final int hash = HashMap.hash(key);
            Node<K,V>[] tab; Node<K,V> first; int n, i;
            int binCount = 0;
            TreeNode<K,V> t = null;
            Node<K,V> old = null;
            // 容量超出阈值或未初始化,则执行扩容操作
            if (size > threshold || (tab = table) == null ||
                    (n = tab.length) == 0) {
                n = (tab = resize()).length;
            }
            // 读取首节点
            if ((first = tab[i = n - 1 & hash]) != null) {
                if (first instanceof TreeNode) {
                    old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
                } else {
                    Node<K,V> e = first; K k;
                    do {
                        if (e.hash == hash &&
                                ((k = e.key) == key || key != null && key.equals(k))) {
                            old = e;
                            break;
                        }
                        ++binCount;
                    } while ((e = e.next) != null);
                }
            }
            // 1)旧值不为 null
            if (old != null) {
                V v;
                // 新值也不为 null
                if (old.value != null) {
                    final int mc = modCount;
                    // 基于函数式接口计算合并值
                    v = remappingFunction.apply(old.value, value);
                    if (mc != modCount) {
                        throw new ConcurrentModificationException();
                    }
                } else {
                    // 新值为 null,则使用旧值
                    v = value;
                }
                // 旧值或合并值不为 null
                if (v != null) {
                    // 则更新旧值
                    old.value = v;
                    afterNodeAccess(old);
                } else {
                    // 否则删除该节点
                    removeNode(hash, key, null, false, true);
                }
                return v;
            }
            // 2)旧值为 null,新值不为 null
            if (value != null) {
                // 2-1)如果是红黑树结构,则新增树节点
                if (t != null) {
                    t.putTreeVal(this, tab, hash, key, value);
                // 2-2)否则新增链表节点
                } else {
                    tab[i] = newNode(hash, key, value, first);
                    // 单向链表元素个数超出树化阈值 8
                    if (binCount >= TREEIFY_THRESHOLD - 1) {
                        // 执行红黑树化操作
                        treeifyBin(tab, hash);
                    }
                }
                ++modCount;
                ++size;
                afterNodeInsertion(true);
            }
            return value;
        }
    
    • HashMap 的计算操作
        /**
         * 键存在,并且值不为 null,则根据函数式接口基于旧键和旧值计算新值,
         * 1)新值不为 null,则更新值,并返回新值
         * 2)新值为 null,则删除节点,并返回 null
         * 键不存在或键关联的值为 null,则直接返回 null,无任何操作。
         */
        @Override
        public V computeIfPresent(K key,
                BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
            if (remappingFunction == null) {
                throw new NullPointerException();
            }
            Node<K,V> e; V oldValue;
            final int hash = HashMap.hash(key);
            // 键存在并且旧值不为 null
            if ((e = getNode(hash, key)) != null &&
                    (oldValue = e.value) != null) {
                final int mc = modCount;
                // 根据函数式接口基于旧键和旧值计算新值
                final V v = remappingFunction.apply(key, oldValue);
                if (mc != modCount) { throw new ConcurrentModificationException(); }
                // 1)新值不为 null,则更新值
                if (v != null) {
                    e.value = v;
                    afterNodeAccess(e);
                    return v;
                } else {
                    removeNode(hash, key, null, false, true);
                }
            }
            return null;
        }
    
        /**
         * 1)如果键值对已经存在,并且值不为 null,则不进行任何操作。
         */
        @Override
        public V computeIfAbsent(K key,
                Function<? super K, ? extends V> mappingFunction) {
            if (mappingFunction == null) {
                throw new NullPointerException();
            }
            final int hash = HashMap.hash(key);
            Node<K,V>[] tab; Node<K,V> first; int n, i;
            int binCount = 0;
            TreeNode<K,V> t = null;
            Node<K,V> old = null;
            if (size > threshold || (tab = table) == null ||
                    (n = tab.length) == 0) {
                n = (tab = resize()).length;
            }
            if ((first = tab[i = n - 1 & hash]) != null) {
                if (first instanceof TreeNode) {
                    old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
                } else {
                    Node<K,V> e = first; K k;
                    do {
                        if (e.hash == hash &&
                                ((k = e.key) == key || key != null && key.equals(k))) {
                            old = e;
                            break;
                        }
                        ++binCount;
                    } while ((e = e.next) != null);
                }
                V oldValue;
                // 1)如果键值对已经存在,并且值不为 null,则不进行任何操作。
                if (old != null && (oldValue = old.value) != null) {
                    afterNodeAccess(old);
                    return oldValue;
                }
            }
            // 2)键不存在,或关联的值为 null
            final int mc = modCount;
            // 基于键计算新值
            final V v = mappingFunction.apply(key);
            if (mc != modCount) { throw new ConcurrentModificationException(); }
            // 2-1)新值为 null,直接返回 null
            if (v == null) {
                return null;
                // 2-2)键存在旧值为 null,新值不为 null,则更新旧值并返回新值。
            } else if (old != null) {
                old.value = v;
                afterNodeAccess(old);
                return v;
            }
            // 2-3)键不存在,新值不为 null,并且 bucket 为红黑树,则插入树节点
            else if (t != null) {
                t.putTreeVal(this, tab, hash, key, v);
            } else {
                // 2-4)键不存在,新值不为 null,并且 bucket 为链表,则插入链表节点
                tab[i] = newNode(hash, key, v, first);
                if (binCount >= TREEIFY_THRESHOLD - 1) {
                    treeifyBin(tab, hash);
                }
            }
            modCount = mc + 1;
            ++size;
            afterNodeInsertion(true);
            return v;
        }
    
        /**
         * 1)键存在,新值为不为 null,则更新值,新值为 null,则删除节点
         * 2)键不存在,则执行插入操作
         */
        @Override
        public V compute(K key,
                BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
            if (remappingFunction == null) {
                throw new NullPointerException();
            }
            final int hash = HashMap.hash(key);
            Node<K,V>[] tab; Node<K,V> first; int n, i;
            int binCount = 0;
            TreeNode<K,V> t = null;
            Node<K,V> old = null;
            if (size > threshold || (tab = table) == null ||
                    (n = tab.length) == 0) {
                n = (tab = resize()).length;
            }
            if ((first = tab[i = n - 1 & hash]) != null) {
                if (first instanceof TreeNode) {
                    old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
                } else {
                    Node<K,V> e = first; K k;
                    do {
                        if (e.hash == hash &&
                                ((k = e.key) == key || key != null && key.equals(k))) {
                            old = e;
                            break;
                        }
                        ++binCount;
                    } while ((e = e.next) != null);
                }
            }
            // 键不存在或旧值为 null 时,oldValue 为 null
            final V oldValue = old == null ? null : old.value;
            final int mc = modCount;
            final V v = remappingFunction.apply(key, oldValue);
            if (mc != modCount) { throw new ConcurrentModificationException(); }
            // 1)键已经存在
            if (old != null) {
                // 1-1)新计算值不为 null,则更新值
                if (v != null) {
                    old.value = v;
                    afterNodeAccess(old);
                // 1-2)新计算值为 null,则删除节点 
                } else {
                    removeNode(hash, key, null, false, true);
                }
            }
            // 2)键不存在
            else if (v != null) {
                // 2-1)如果目标 bucket 是红黑树,则执行树节点插入
                if (t != null) {
                    t.putTreeVal(this, tab, hash, key, v);
                // 2-2)执行链表节点插入 
                } else {
                    tab[i] = newNode(hash, key, v, first);
                    if (binCount >= TREEIFY_THRESHOLD - 1) {
                        treeifyBin(tab, hash);
                    }
                }
                modCount = mc + 1;
                ++size;
                afterNodeInsertion(true);
            }
            return v;
        }
    
  • 相关阅读:
    1065-两路合并
    1064-快速排序
    1063-冒泡排序
    1062-直接插入排序
    1061-简单选择排序
    1058-Tom and Jerry
    关于WinForm引用WPF窗体
    ref与out的区别
    看到他我一下子就悟了(续)---委托
    域名的a记录转过来他的公网ip
  • 原文地址:https://www.cnblogs.com/zhuxudong/p/10012391.html
Copyright © 2020-2023  润新知