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;
    }

posted on 2018-11-24 15:55  竺旭东  阅读(124)  评论(0编辑  收藏  举报

导航