Java 数据结构 -- 15.Java 8 数据结构 HashMap

本文深入剖析 Java 8 中 HashMap 的实现,探讨其内部数据结构,包括与 AVL 树、B 树、2-3-4 树和红黑树的关系。讲解 HashMap 如何利用红黑树平衡性能,以及在 Map 结构中的应用和操作特性,如桶、视图、冲突解决和迭代器。同时,概述了 HashMap 中的树化概念及其影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

书接上文,上一篇中对 Map 接口与 AbstractMap 抽象类进行了介绍与分析,本篇将对 Map 接口的最终实现类 HashMap 做介绍与分析。

Map 的继承结构图:
在这里插入图片描述
这里可以看到,HashMap 直接继承了 AbstractMap 抽象类,实现了 Map 接口,所以接下来可以直接查看 HashMap 的源码。

由于在 HashMap 中还涉及到树化(红黑树)的概念,但是在了解红黑树之前,还要先了解一下 AVL 树(平衡二叉树),B 树, 2-3-4 树的概念,所以这里先对这些树概念依次了解:

AVL(平衡二叉树)

在计算机科学中,AVL树是最早被发明的自平衡二叉查找树。在AVL树中,任一节点对应的两棵子树的最大高度差为1,因此它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下的时间复杂度都是 O(log n)。增加和删除元素的操作则可能需要借由一次或多次树旋转,以实现树的重新平衡。AVL树得名于它的发明者G. M. Adelson-Velsky和Evgenii Landis,他们在1962年的论文《An algorithm for the organization of information》中公开了这一数据结构。
节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。 摘自 Wiki

平衡二叉树也是 Mysql 数据库 innoDB 索引中进化重要的一环,在 数据库篇 中将介绍与分析,此处只需要知道平衡二叉树是基于二叉树的基础上,限制每个节点的平衡因子(-1 到 1,通过左右子树的高度差,包括可能存在的子树)递归旋转得到的,它保证了平均与最差的复杂度都是 O(log n)。

B 树

在计算机科学中,B树(英语:B-tree)是一种自平衡的树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除的动作,都在对数时间内完成。B树,概括来说是一个一般化的二叉查找树(binary search tree)一个节点可以拥有最少2个子节点。与自平衡二叉查找树不同,B树适用于读写相对大的数据块的存储系统,例如磁盘。B树减少定位记录时所经历的中间过程,从而加快存取速度。B树这种数据结构可以用来描述外部存储。这种数据结构常被应用在数据库和文件系统的实现上。摘自 Wiki

平衡二叉树也是 Mysql 数据库 innoDB 索引中进化重要的一环,在 数据库篇 中将介绍与分析,此处只需要知道 B 树平衡二叉树针对操作系统进行的优化,它拥有二叉树的特性,并且不再局限于二叉,而是可以根据操作系统一次文件操作的块大小拥有更多的子树。

2-3-4 树

2-3-4 树在计算机科学中是阶为 4 的B树。
大体上同B树一样,2-3-4 树是可以用做字典的一种自平衡数据结构。它可以在 O(log n) 时间内查找、插入和删除,这里的 n 是树中元素的数目。
2-3-4 树在多数编程语言中实现起来相对困难,因为在树上的操作涉及大量的特殊情况。红黑树实现起来更简单一些,所以可以用它来替代。摘自 Wiki

这里就与数据库中的 innoDB 开始不同了,数据库中的 B 树经过优化进阶为了 B+ 数,在 数据库篇 中将对 B+ 数进行介绍与分析,此处只需要知道 2-3-4 数是一颗阶数位 4 的 B 数,阶数就是一颗树可以拥有的子树的个数。拥有 B 树的所有特性,只是阶位 4。

红黑树(英语:Red–black tree)是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它在1972年由鲁道夫·贝尔发明,被称为"对称二叉B树",它现代的名字源于Leo J. Guibas和Robert Sedgewick于1978年写的一篇论文。红黑树的结构复杂,但它的操作有着良好的最坏情况运行时间,并且在实践中高效:它可以在 O(log n) 时间内完成查找,插入和删除,这里的 n 是树中元素的数目。
红黑树和AVL树一样都对插入时间、删除时间和查找时间提供了最好可能的最坏情况担保。这不只是使它们在时间敏感的应用,如实时应用(real time application)中有价值,而且使它们有在提供最坏情况担保的其他数据结构中作为基础模板的价值;例如,在计算几何中使用的很多数据结构都可以基于红黑树实现。
红黑树在函数式编程中也特别有用,在这里它们是最常用的持久数据结构(persistent data structure)之一,它们用来构造关联数组和集合,每次插入、删除之后它们能保持为以前的版本。除了 O(log n) 的时间之外,红黑树的持久版本对每次插入或删除需要 O(log n) 的空间。
红黑树是2-3-4树的一种等同。换句话说,对于每个2-3-4树,都存在至少一个数据元素是同样次序的红黑树。在2-3-4树上的插入和删除操作也等同于在红黑树中颜色翻转和旋转。这使得2-3-4树成为理解红黑树背后的逻辑的重要工具,这也是很多介绍算法的教科书在红黑树之前介绍2-3-4树的原因,尽管2-3-4树在实践中不经常使用。
红黑树相对于AVL树来说,牺牲了部分平衡性以换取插入/删除操作时少量的旋转操作,整体来说性能要优于AVL树。
性质:
红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
节点是红色或黑色。
根是黑色。
所有叶子都是黑色(叶子是NIL节点)。
每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
这些约束确保了红黑树的关键特性:从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。
要知道为什么这些性质确保了这个结果,注意到性质4导致了路径不能有两个毗连的红色节点就足够了。最短的可能路径都是黑色节点,最长的可能路径有交替的红色和黑色节点。因为根据性质5所有最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长。
在很多树数据结构的表示中,一个节点有可能只有一个子节点,而叶子节点包含数据。用这种范例表示红黑树是可能的,但是这会改变一些性质并使算法复杂。为此,本文中我们使用"nil叶子"或"空(null)叶子",如上图所示,它不包含数据而只充当树在此结束的指示。这些节点在绘图中经常被省略,导致了这些树好像同上述原则相矛盾,而实际上不是这样。与此有关的结论是所有节点都有两个子节点,尽管其中的一个或两个可能是空叶子。
摘自 Wiki

简单路径是图论中的概念,就是一条没有重复节点的卢纶。
这里注意虽然每棵 2-3-4 树(4 阶 B 树)都有等价的红黑树,但是红黑树是二叉的。这里 Wiki 中提示了:要知道为什么这些性质确保了这个结果,注意到性质4导致了路径不能有两个毗连的红色节点就足够了。最短的可能路径都是黑色节点,最长的可能路径有交替的红色和黑色节点。因为根据性质5所有最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长。这样就保证了红黑树基本上是平衡的,而对于它的操作的效率基本是相等的。

对 HashMap 中设计到的树的概念了解这些就可以了,下面开始阅读 HashMap 源码:

/**
 * 基于实现 Map 接口的 Hash table。这个实现类提供了所有可选操作,并且允许 null 值和 null 键。
 * (HashMap 类大致与 HashTable 相同,除了它是线程不安全的并且允许 null。)这个类不保证了 map 的顺
 * 序;  
 * 特指它不能保证顺序一直不变。
 * 
 * 这个实现那类对于基础操作(get 和 put)提供了时间恒等的性能,假设在 hash 功能将元素正确地分布在不同的* 
 * 桶内。对于 collection 视图的迭代操作需要与 HashMap 实例(桶数)加上它的长度(键值映射数量)的“容
 * 量”成比例的操作时间。因此,如果迭代性能很重要的话不要讲初始容量设置的太高是很重要的(或者加载因子太
 * 低)。
 *
 * 有两个参数会影响一个 HashMap 实例的性能:初始容量和加载因子。容量是 hash table 中桶的个数,初始容量
 * 就简单的是 hash table 在建立时的容量。加载因子是一个度量在 hash table 自动增加之前,它的容量有多满
 * 测值。当 hash table 中条目的数量超过加载因子和当前容量的结合,hash table 被 rehashed(意味着,内
 * 部数据结构被重新构造)来使 hash table 拥有大约两倍的桶数。
 *
 * 就如同一般的规则,默认加载因子(0.75)为时间与空间消耗的提供了一个很好的权衡。更高的值减少了空间开销但
 * 是增加了查找开销(影响到了 HashMap 中的大部分操作,包括 get 和 put)。map 中条目数量的期望值与它的
 * 加载因子应该在设置初始化容量的时候就考虑好,来最小化 rehash 操作的次数。如果初始化容量比条目除以加载因
 * 子的最大值还要大,根本不会发生 rehash 操作。
 *
 * 如果 HashMap 实例中的多个映射关系要被排序,用一个很大的容量来建立它将允许映射排序比让它自动在需要扩大 
 * table 的时候 rehashing 更加高效。注意使用许多相同 {@code hashCode()} 的键会理所当然的降低任何 
 * hash table 的性能。为了改善碰撞,当键时 {@link Comparable} 的时候,这个类可以在键s中使用比较顺序
 * 来帮助断绝。
 *
 * 注意这个实现类不是线程安全的。如果多个线程并发地访问一个 hash map,并且至少有一个线程结构性地改变了 
 * map,它必须从外部实现线程安全。(一个结构性改变是指任何添加或者删除一个或者多个映射关系;仅仅修改一个实
 * 例已经包含的键关联的值不是一个结构性改变。)典型的实现方式是通过对于一个自然封装了 map 的对象进行线程安
 * 全化。
 *
 * 如果不存在这样的对象,map 应该被使用
 * {@link Collections#synchronizedMap Collections.synchronizedMap}
 * 方法封装。这最好在创建的时候完成,来防止意外的非线程安全的访问 map:<pre>
 *   Map m = Collections.synchronizedMap(new HashMap(...));</pre>
 *
 * 所有类的 “collection 视图方法”返回的迭代器是 fail-fast 的:如果 map 在迭代器创建后的任何时间点发生
 * 了结构性的改变,以除了通过迭代器自身 remove 方法的任何方式,迭代器将抛出一个 {@link 
 * ConcurrentModificationException}。因此,对于并发修改,迭代器失败的快速与干净,而不是在未来某个不
 * 确定的时间点冒险做任何不确定的行为。
 *
 * 注意一个迭代器的 fail-fast 行为不能提供像它所描述的那样的保证,一般来说,不可能对于不同步的并发操作做
 * 任何硬性的保证。基于最好性能的基础,fail-fast 迭代器抛出一个 ConcurrentModificationException。
 * 因此,编写一个依赖于这个异常来保证其正确性的程序是错误的:迭代器的 fail-fast 行为应该只被用于检查 
 * bugs。
 *
 * 这个类的注释先后改了三次,因为其中有太多难以理解的细节了,全部写上反而会因为无用信息太多而更难以理解,再
 * 加上本人理解与翻译能力有限,所以优化了三次注释,目前只对重要的类,方法,属性,以及重要的实现部分做标注,
 * 重复的地方也不再标注。对于方法与类属性,标注了方法与属性的意义,以及对于 JavaDoc 的翻译,翻译可能不
 * 对,但是意义应该没有问题,对于特定名称保留英文名称。
 *
 * HashMap 对于在条目上使用了数组 + 链 + 红黑树三种结构,对于树化节点与容器的操作的实现方式非常复杂,细
 * 节不做翻译(因为要搞清楚这些代码的成本有点高。。)
 */
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

    private static final long serialVersionUID = 362498820763181265L;

    /*
     * 实现类注意(这一段我已经很努力翻译了,但是由于对于实现的不理解,所以翻译的还是比较差,可以跳过不
     * 看)
     *
     * 这个 map 通常充当一个桶化的 hash table,但是当桶变得太大时,它们会在容器中转化为 TreeNodes,每
     * 一个都被构造成类似于那些 java.util.TreeMap 的结构。大部分方法在适用的时候(简单地通过检查一个 
     * node 的实例)尽力去使用普通的容器,而不是转发到 TreeNode 方法。TreeModes 的容器可以想其他(的
     * 容器)一样被穿过和使用,但是额外支持(容器)过多时候的更快速的查找。但是,既然在平时使用过程中大多
     * 数容器是没有过多的,在 table 方法(调用)期间可以延迟检查 tree 容器的存在。
     *
     * Tree 容器(比如,元素都是 TreeNodes 的容器)首先通过 hashCode 排序,但是当发生碰撞的时,如果两
     * 个元素同样是 “class C immplements Comparable<C>” 类型那么他们的 compareTo 方法被用来排
     * 序。(我们谨慎地通过反射检查泛型来验证这个--查看 comparableClassFor 方法)。tree 容器增加的复
     * 杂度在当键既不是唯一 hashs 或者有序的时候提供最差情况 O(log n) 操作是值得的,因此,性能在 
     * hashCode() 方法返回的值被分散的很差时意外或者故意使用(容器)时性能优雅的降低,以及很多键分享一个 
     * hashCode 的情况,只要它们也是 Comparable 的。(如果这些中没有一个应用了,与不采取预防措施相比,
     * 我们可能浪费了大概一到两倍的时间和空间。但是对于已知的来自于糟糕的用户编程实践而引起的缓慢来说,这
     * 影响很小。赤裸裸的嘲讽。。)
     *
     * 因为 TreeNode 差不多是普通 nodes 的两倍大小,我们只有在容器为了保证使用(查看 
     * TREEIFY_THRESHOLD)包含了足够多的 nodes 时才使用它们。并且当它们变得太小(因为移除或者重建)它
     * 们将被重新转换成一个普通的容器。在于很好的分布了的用户 hashCodes 一起使用时。tree 容器是很少被使
     * 用的。理想的是,在随机 hashCode 下,容器中的 nodes 的频率跟随一个泊松分布
     * (http://en.wikipedia.org/wiki/Poisson_distribution)对于默认重构门槛 0.75,平均参数大概
     * 是 0.5,虽然由于重构颗粒会伴随一个巨大的变动。无视变动,期望的 list 大小的发生是 (exp(-0.5) * 
     * pow(0.5, k) /factorial(k))。第一值是:
     *
     * 0:    0.60653066
     * 1:    0.30326533
     * 2:    0.07581633
     * 3:    0.01263606
     * 4:    0.00157952
     * 5:    0.00015795
     * 6:    0.00001316
     * 7:    0.00000094
     * 8:    0.00000006
     * 更多:这一千万种种比 1 更小
     *
     * 一个 tree 容器的跟通常是它的第一个 node。但是,有时候(目前只在 Iterator.remove 上),根可能
     * 在别处,但是可以被以下父类链接 (method TreeNode.root())。
     *
     * 所以内部应用方法接收一个 hash 值作为一个参数(通常有一个 public 方法提供),允许它们调用对方而不
     * 用重新计算用户 hashCodes。大部分内部方法也接收一个 “tab” 参数,它通常是当前 table,但是可能在
     * 重构或者转换的时候变成一个新的或者旧的。
     *
     * 当容器链表是 treeified,分隔,或者 untreeified,我们将它们保持在相同的访问/穿过顺序(比如,类
     * 属性Node.next)来更好的保护线程,并且来将分隔和穿过的操作与调用 iterator.remove 稍微简单化。当
     * 在插入种时候 comparators,来通过再平衡保证一个完整的顺序(或者与这里需要的尽量接近),我们比较类
     * s与相等的 Hash 值作为冲突解决。
     *
     * 一般的和 tree 模式下的使用和转变的区别被子类 LinkedhashMap 的存在复杂化了。查看以下的 hook 方
     * 法,这些方法定义为在插入,移除和访问时调用,从而允许 LinkedHashMap 内部以其它方式保持对于这些机
     * 制的独立性。(这还需要传递一个 map 实例给可能创建新 nodes 的方法。)
     *
     * SSA 基础的并行编程式样编程风格帮助避免在所有扭曲指针中出现混叠错误。
     */


		/**
		 * 注意:以下变量的选择都涉及到各种复杂的权衡,这里不必完全理解,只要知道他们是什么,以及作用是什
		 * 么就可以了
		 **/
		
    /**
     * 默认初始化容量 
     * 
     * 必须是 2 的幂。
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    /**
     * 最大容量
     *
     * 在无论是构造器的哪个参数隐式的被指定是更大的值的时候使用。
     * 必须是 2 的 <= 1<<30 次方。
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * 默认加载因子
     * 
     * 当没有指定在构造器中指定时使用的加载因子。
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * 树化阈值(对应于转换为 TreeNode)
     *
     * 容器用以树而不是一个列表来计算容器的阈值。在添加一个元素到至少有这么多 nodes 的容器中时容器被转换
     * 成树。该值必须大于2,并且至少应为8,以符合树移除中关于收缩后转换回普通容器的假设。
     */
    static final int TREEIFY_THRESHOLD = 8;

    /**
     * 种化阈值(对应于转换为普通 Node)
     * 
     * 为了在一个重构操作中解冻一个(拆分)容器的容器数量阈值。应该比 TREEIFY_THRESHOLD 小,且在配合在
     * 移除操作下缩小检查最多为 6。
     */
    static final int UNTREEIFY_THRESHOLD = 6;

    /**
     * 最小树化容量
     * 
     * 对于需要被 treeified 的容器的最小 table 容量。(否则如果在容器中有太多的 nodes table 被重
     * 构。)应该是至少 4 * TREEIFY_THRESHOLD 来避免重构与树化阈值之间的复杂性。
     */
    static final int MIN_TREEIFY_CAPACITY = 64;

    /**
     * 重要内部类 Node
     * 
     * 基础的 hash 容器 node,被大多数条目使用。(在下面查看 TreeNode 子类,并在 LinkedHashMap 中查
     * 看 Entry 子类。)实现了 Map.Entry 接口,是普通条目(树化之前)类。注意在 Entry 接口的源码中可
     * 以看到它是一个单独的对象,而实现它的 Node 就可以看到是一个链式对象。
     */
    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash; //hash
        final K key; //键
        V value; //值
        Node<K,V> next; //下一个,所以 Node 的结构类似于单向链表

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public final K getKey()        { return key; } 
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }

        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

		/**替换条目中的值**/
        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue())) //分别比较键和值的相等性
                    return true;
            }
            return false;
        }
    }

    /* ---------------- 静态工具 -------------- */

    /**
     * 计算 key.hashCode() 并且散开(XORs)较高的 hash 的比特位到较低。因为 table 使用 2 的幂次方掩
     * 码,仅在当前掩码上方以位为单位变化的哈希集将始终发生冲突。(在已知的例子中,一组浮动键在小表中保持
     * 连续的整数。)所以我们应用了一种将较高的比特位向下降低的转换。在速度,工具性和比特的传播性之间有一
     * 个权衡。因为很多一般的 hahes 的 sets 已经明智的分布了(所以从传播中收益),而且因为我们使用 
     * trees 来解决容器中的巨大的 sets 的碰撞,我们只要以最廉价的可能的方式方式 XOR 一些改变过的比特值
     * 来减少语义的损失,也能合并最高的因为 table 接线而永远不会在索引计算中被使用到的比特位的影响。
     */
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

    /**
     * 如果 x 的类型是符合 “class C implements Comparable<C>” 则返回它的类型,否则返回 null。
     */
    static Class<?> comparableClassFor(Object x) {
        if (x instanceof Comparable) {
        	/***类 c,类型数组 ts,as,类型 t,范型类型 p**/
            Class<?> c; Type[] ts, as; Type t; ParameterizedType p; 
            if ((c = x.getClass()) == String.class) //分支检查
                return c;
            if ((ts = c.getGenericInterfaces()) != null) {
                for (int i = 0; i < ts.length; ++i) {
                    if (((t = ts[i]) instanceof ParameterizedType) &&
                        ((p = (ParameterizedType)t).getRawType() ==
                         Comparable.class) &&
                        (as = p.getActualTypeArguments()) != null &&
                        as.length == 1 && as[0] == c) //c 是泛型类型参数
                        return c;
                }
            }
        }
        return null;
    }

    /**
     * 如果 x 的类型是符合 “class C implements Comparable<C>” 则返回它的类型,否则返回 null。
     */
    @SuppressWarnings({"rawtypes","unchecked"}) //为了转换成 Comparable
    static int compareComparables(Class<?> kc, Object k, Object x) {
        return (x == null || x.getClass() != kc ? 0 : //这里需要将 x 的类与 kc 做比较,不明白为,比价成功才会调用 compareTo 方法,不明白为什么需要 kc。。
                ((Comparable)k).compareTo(x));
    }

    /**
     * 返回给定对象容量的一个长度(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;
    }

    /* ---------------- 类属性 -------------- */

    /**
     * 当前 HashMap 持有的 Node 数组(包含 Node 和 TreeNode)
     * 
     * Table,在第一次使用时被初始化,并在需要是重构。当分配后,长度总是 2 的幂次方。(我们在一些操作中
     * 也容许 0 长度来允许现在已经不需要的引导算法。)     
     */
    transient Node<K,V>[] table;

    /**
     * 当前 HashMap 持有的 Map.Entry(条目) set
     *
     * 持有缓存的 entrySet()。注意 AbstractMap 的类属性被 keySet() 和 values() 使用。之前在 
     * AbstractMap 中看到的最重要的属性 entrySet
     */
    transient Set<Map.Entry<K,V>> entrySet;

    /**
     * 当前 HashMap 的长度
     *
     * 当前 map 中包含的键值映射数量。
     */
    transient int size;

    /**
     * 结构性改变次数
     * 
     * 当前 HashMap 已经被结构性修改的次数,是哪些更高了 HashMap 中映射的数量或者不然就是修改了它的内
     * 部结构(比如 rehash)。这个类属性被用来建立 HashMap 的 Collection 视图的迭代器 fail-fast。
     * (查看 ConcurrentModificationException)。
     */
    transient int modCount;

    /**
     * 当前 HashMap 的阈值
     * 
     * 下一个重构的大小值(容量 * 加载因子)
     * 
     * @serial
     */
    // (这个 java 文档描述是真正的序列化。另外,如果 table 数组没有被分配。这个类属性持有初始化数组容量,或者 0 表示 DEFAULT_INITIAL_CAPACITY。)
    int threshold;

    /**
     * 当前 hash table 的加载因子
     *
     * @serial
     */
    final float loadFactor;

    /* ---------------- 公共操作 -------------- */

    /**
     * HashMap 双参(初始化容量和加载因子)构造方法
     * 
     * 使用指定的初始化容量和加载因子构造一个空的 HashMap。
     */
    public HashMap(int initialCapacity, float loadFactor) {
    	//检查初始化容量,限制容量范围
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        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 = tableSizeFor(initialCapacity);
    }

    /**
     * HashMap 单参(初始化容量)构造器
     * 
     * 使用指定初始化容量和默认加载因子(0.75)构造一个空的 HashMap。
     */
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

    /**
     * HashMap 无参构造方法
     * 
     * 使用默认初始化容量(16)和默认加载因子(0.75)构造一个空的 HashMap。
     */
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; //所有其他类属性都是默认的
    }

    /**
     * HashMap 单参(另一个 Map)构造方法
     * 
     * 使用一个指定 Map 相同的映射s构造一个新的 HashMap。HashMap 被使用默认加载因子(0.75)和足够装下
     * 指定 Map 映射s的初始化容量构造。
     */
    public HashMap(Map<? extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        putMapEntries(m, false);
    }

    /**
     * 塞入所有条目方法
     * 
     * 实现 Map.putAll 和 Map 构造器(单参(另一个 Map)构造方法)。     
	 */
    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        int s = m.size();
        //检查 m 的长度
        if (s > 0) {
            if (table == null) { //在 size 之前
                float ft = ((float)s / loadFactor) + 1.0F;
                //缓存容量
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                         (int)ft : MAXIMUM_CAPACITY);
                //根据容量计算阈值
                if (t > threshold)
                    threshold = tableSizeFor(t);
            }
            else if (s > threshold)
                resize(); //容量大于阈值,重构当前 map
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) { //遍历参数 map,塞入当前 map
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }

    /**
     * 或当当前 HashMap 长度方法
     * 
     * 返回当前 map 中键值映射的数量。
     */
    public int size() {
        return size;
    }

    /**
     * 判空方法
     * 
     * 如果当前 map 不包含键值映射,返回 true。
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 键查值方法
     * 
     * 返回指定键映射到的值,或者如果 map 中欧冠不包含这样的键就返回 null。
     * 
     * 更正式地说,如果 map 包含一个这样的键 k 到这样的值 v: {@code (key==null ? k==null :
     * key.equals(k))},那么这个方法返回 v;不然的话它返回 null(至多只有一个这样的映射。)
     *
     * 一个 null 返回值不是必然意味着 map 中不包含这个键的映射;它也可能明确地标示存在键到 null 的映射。#containsKey 操作可以用来区分这两种情况。
     */
    public V get(Object key) {
    	//获取 Node,返回 Node 的值
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }

    /**
     * 获取 Node 方法
     * 
     * 实现 Map.get 和相关方法。
     * 
     * 方法中重要实现片段 tab[(length - 1) & hash] 参考了 
     * https://www.jianshu.com/p/2d86f83d1d26 文章,此处做下搬运,这样计算索引要插入的位置好处主要
     * 有两个:
     * 1、时一定不会发生数组越界,因为 table 的长度规定时间 2 的幂次方,转成二进制一定是 1000...0(1 
     * 后面偶数个0),那么 length - 1 一定是 0111...1(0 后面偶数个1),这时候发生位与结果值一定不会
     * 比原值大,所以是一定不会越界的。
     * 2、使得元素分布更加均匀,由之前的分析可知,table 的长度一定是偶数,length - 1 一定是奇数,假设
     * 现在数组的长度(table.length)是 16,减去 1 (length - 1)就是 15,15 对应的二进制值时 
     * 1111。现在有两个元素需要插入,一个 Hash 值为 8(二进制值时 1000),一个为 9(二进制值时 
     * 1001),那么它们与(length - 1)位与后就是 1000 和 1001,被分配在数组的不同位置,这样是比较均
     * 匀的。如果数组长度是奇数,比如 15,那么 length - 1 就是 14,对应二进制值时 1110,对于上面两个 
     * Hash 值位与后得到的结果都是 1000,它们被分配在数组的相同位置,不够均匀。
     */
    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) { //判断并计算要插入数组的位置值
            if (first.hash == hash && //总是检查第一个 Node(这里检查第一个的意思相当于当前桶中只有一个数据,这是大概率时间,如果命中直接返回)
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) { //到了这一步已经确定 Node 存在并且 Node 的首元素不是需要的元素了
                if (first instanceof TreeNode) //判断 Node 是否为 TreeNode,如果是,则执行获取 TreeNode 逻辑
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do { //到这里,Node 就有 next,又不是 TreeNode,通过 e.next 循环遍历 Node,直到找到需要的结果
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

    /**
     * 判存方法(键)
     * 
     * 如果当前 map 包含一个指定键的映射,返回 true。
     */
    public boolean containsKey(Object key) {
    	//直接调用 getNode 判断结果是否存在
        return getNode(hash(key), key) != null;
    }

    /**
     * 塞入/更新方法
     *
     * 将当前 map 中的指定键与指定值连接。如果 map 之前包含一个键的映射,旧值将被替换。
     */
    public V put(K key, V value) {
    	//直接调用 putVal 方法
        return putVal(hash(key), key, value, false, true);
    }

    /**
     * 塞值方法
     * 
     * 实现 Map.put 和相关方法。为什么不直接写在 put 方法里?这种设计的意义没有明白。。
     * 
     * onlyIfAbsent 参数的含义是是否要更换值,如果为 true,则不改变已经存在值。
     * evict 参数的含义是调用时间,如果在创建 HashMap 时候调用则为 true,创建后调用为 false。
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        //这种多用局部中间变量的方式可以理解为作者的编码风格,这样做的好处是
        //1、中间变量,用于暂存,原值可能被替换
        //2、成员变量皆为全名,局部变量可用缩写
        //3、配合变量在方法中实际的含义重命名
        //4、便于调试
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //若 Node 数组为空或者 Node 数组中没有元素,重构计算长度
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;    
        if ((p = tab[i = (n - 1) & hash]) == null) //如果 Node 数组中 hash 对应的位置的 Node 为空
            tab[i] = newNode(hash, key, value, null); //构造 Node 并插入数组最后位置
        else {
            Node<K,V> e; K k;
            if (p.hash == hash && //此处已经肯定 Node 数组中 hash 对应的位置的 Node 不为空
                ((k = p.key) == key || (key != null && key.equals(k)))) //如果这个 Node 的键与参数相同,还是检查 Node 的首元素 
                e = p;  //e 就不为 null 了
            else if (p instanceof TreeNode) //如果该 Node 是 TreeNode 实例
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); //调用 TreeNode 的塞值方法
            else { //否则说明该 Node 存在 next 
                for (int binCount = 0; ; ++binCount) { //容器累加器
                    if ((e = p.next) == null) { //链式遍历结束,将这个判断写在前面是因为这种情况 Node 存在后续节点的情况不常发生,如果当前遍历到的 Node 依然存在后续节点,则走下面的 if,直到最后一个 Node,如果都没有找到,那么进入这个 if 分支时 e 一定是 null,意味着没有找到对应的映射
                        p.next = newNode(hash, key, value, null); //构造新 Node 并链接在当前遍历到的 Node 之后
                        if (binCount >= TREEIFY_THRESHOLD - 1) //判断当前累加器是否大于等于树化阈值 - 1,第一个给 -1
                            treeifyBin(tab, hash); //树化容器
                        break; //由于已经是链的最后一环,跳出
                    }
                    if (e.hash == hash && //此时 e 已经是遍历链的后一个 Node
                        ((k = e.key) == key || (key != null && key.equals(k)))) //如果 e 的键与参数相同
                        break; //跳出循环
                    p = e; //走到这里说明没有遍历到最后一环 Node,并且当前 Node 的键与参数不同,不是同一个 Node,更新步移参数 p
                }
            }
            if (e != null) { //根据上面的分析,存在键对应的映射时,e 一定不是 null
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null) //旧值可以改变或者旧值是 null
                    e.value = value; //改变 e 的旧值
                afterNodeAccess(e); //这个方法还没出现,等待后续分析
                return oldValue; //返回旧值
            }
        }
        ++modCount; //发生了结构性的改变,替换旧值时会直接返回 oldValue,其他情况都属于在创建或者添加后续节点,结构都发生了改变,所以需要自增 modCOunt 和 size
        if (++size > threshold) //自增长度,如果长度大于阈值
            resize(); //重构当前 HashMap
        afterNodeInsertion(evict); //这个方法还没出现,等待后续分析
        return null; //没有旧值,返回 null
    }

    /**
     * 重要方法,重构方法
     * 
     * 初始化 table 长度或者将 table 长度翻倍。如果是 null,使用类属性 threshold 持有的初始化容量对
     * 象来分配。不然的话,因为我们使用 2 的幂次方扩展,每个容器中的元素必须要么是在新的 table 中保持在
     * 相同索引位置,要么是移动到 2 的幂次方的偏移量。
     */
    final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        //旧容量就是旧数组长度
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        //旧阈值
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) { //如果旧容量大于 0
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE; //如果旧阈值大于 MAXIMUM_CAPACITY,限制为 Integer.MAX_VALUE
                return oldTab; //直接返回旧数组
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY) //如果旧阈值翻倍后小于 MAXIMUM_CAPACITY 且旧阈值大于等于 DEFAULT_INITIAL_CAPACITY
                newThr = oldThr << 1; //新阈值等于旧阈值翻倍,如果旧阈值为 0,那么新阈值还是 0,情况 1 
        }
        else if (oldThr > 0) //次数旧长度小于等于 0,说明是初始化,但是旧阈值大于 0 
            newCap = oldThr; //用旧阈值作为初始化容量
        else {               //如果旧容量小于等于 0,旧阈值也小于等于,表示新容量与新阈值都使用默认值
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) { //如果新阈值等于 0,也就是上述判断种的情况 1
            float ft = (float)newCap * loadFactor; //新容量乘以加载因子,作为新阈值缓存
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE); //在加载因子大于 1 的情况下会出现 ft 比新容量大的情况,所以两个都要与 MAXIMUM_CAPACITY 比较做限制,计算新阈值
        }
        threshold = newThr; //替换当前 HashMap 的阈值
        @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; //分配空的 Node 数组
        table = newTab; //替换当前 HashMap 的 Node 数组
        if (oldTab != null) { //当旧 Node 数组不为 null 时候
            for (int j = 0; j < oldCap; ++j) { //遍历数组
                Node<K,V> e;
                if ((e = oldTab[j]) != null) { //如果遍历到的当前 Node 不为空
                    oldTab[j] = null; //当前 Node 置为 null
                    if (e.next == null) //依然判断 Node 首元素,如果没有后续节点
                        newTab[e.hash & (newCap - 1)] = e; //塞入新数组中重新定位的位置
                    else if (e instanceof TreeNode) //或者如果 e 是 TreeNode 实例 
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); //调用 TreeNode split 方法,这个方法还没有出现,后续分析
                    else { //此处说明 e 有后续节点,并且 e 不是一个 TreeNode
                        Node<K,V> loHead = null, loTail = null; //这里可以认为 loHead 与 loTail 是 lo 链条上的头与尾
                        Node<K,V> hiHead = null, hiTail = null; //这里可以认为 hiHead 与 hiTail 是 hi 链条上的头与尾
                        Node<K,V> next;
                        do {
                            next = e.next;
                            //奇怪这里为什么不用 oldCap - 1。。
                            if ((e.hash & oldCap) == 0) { //由于 Node 数组的长度肯定是 2 的幂次方,以 16 为例(二进制 10000),假设 e 的哈希值为 9(二进制 01001),位与得 0,如果 e 的哈希值为 17(二进制 10001),位与得 10000,对元素进行区分
                                if (loTail == null)  //如果 lo 链尾为 null
                                    loHead = e; //将当前 Node 赋值给 lo 链头,情况 2
                                else //如果 lo 链尾有 Node
                                    loTail.next = e; //将当前 Node 拼接在 lo 链尾后,情况 3
                                loTail = e; //将当前 Node 赋值给 lo 链尾,这里是为了辅助检查 loTail == null,将在循环结束后置为 null,类似于拼接“,”,循环结束后移除最后一个 ","
                            }
                            else { //当前 Node 不在原数组中的第一个位置
                                if (hiTail == null) //如果 hi 链尾为 null
                                    hiHead = e; //当前 Node 赋值给 hi 链头
                                else //如果 hi 链尾有 Node
                                    hiTail.next = e; //将当前 Node 拼接在 hi 链尾后
                                hiTail = e; //将当前 Node 赋值给 hi 链尾,与情况 4 相同
                            }
                        } while ((e = next) != null); //循环条件为当前 Node 存在后续节点
                        //这里就是在原数组 j 位置的 Node,重新分割到了新数组的 j 与 j + oldCap 位置上,更分散了
                        if (loTail != null) { //如果 lo 链尾不为 null
                            loTail.next = null; //将 lo 链尾的后续 Node 置为 null,消除副作用
                            newTab[j] = loHead; //将 lo 链头 Node 塞入新数组中的 j 位置,位置与后续节点都没有变化
                        }
                        if (hiTail != null) { //如果 hi 链尾不为 null
                            hiTail.next = null; //将 hi 链尾的后续 Node 置为 null
                            newTab[j + oldCap] = hiHead; //将 hi 链头 Node 塞入新数组中 j + 旧容量(旧数组长度)的位置,后续节点没有变化,位置发生了变化
                        }
                    }
                }
            }
        }
        return newTab; //返回新的 Node 数组
    }

    /**
     * 树化容器方法
     *
     * 除非 table 过于小,替换给定 hash 值在容器中链接到的所有 nodes,在容器太小的情况下,重构。
     */
    final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize(); //如果容器是 null 或者容器长度小于 MIN_TREEIFY_CAPACITY(最小树化容量),调用重构方法
        else if ((e = tab[index = (n - 1) & hash]) != null) { //获取 hash 对应的数组位置的 Node
            TreeNode<K,V> hd = null, tl = null;
            do {
                TreeNode<K,V> p = replacementTreeNode(e, null); //循环,每次调用 replacementTreeNode 方法将当前 Node 转化为 TreeNode p,这个方法还未出现,稍后分析
                if (tl == null) //只有在循环第一次时可能发生
                    hd = p; //table 数组 hash 对应位置的 Node 的首节点的树化节点赋值给 hd
                else { //循环第一次后,都进入 else
                		//双向更新链接
                    p.prev = tl; //这里出现了 prev,这个类属性在 Node 中是不存在的,可以推断 TreeNode 中加入了前节点属性,类似于一个双向链表了,更新 p 的前节点
                    tl.next = p; //更新 tl 的后节点
                }
                tl = p; //循环第一次,将树化的 Node 首节点赋值给 tl,其后都将后续节点的树化节点赋值给 tl
            } while ((e = e.next) != null); //循环条件是当前节点存在后续节点
            if ((tab[index] = hd) != null) //大部分情况下(除了在在循环第一次树化 table 对应位置 Node 的首节点结果为 null)hd 都不为 null
                hd.treeify(tab); //hd 调用树化方法,这个方法暂时还没有出现,稍后分析
        }
    }

    /**
     * 重要方法,全部塞入/更新方法 
     *
     * 从指定 map 中拷贝所有映射s到当前 map 中。这些映射将替换任何当前 map 中已经有的任何在当前指定 
     * map 中存在的键s。
     */
    public void putAll(Map<? extends K, ? extends V> m) {
        putMapEntries(m, true); //直接调用 putMapEntries 方法,这个方法还未出现,稍后分析
    }

    /**
     * 重要方法,根据键移除方法
     * 
     * 从当前 map 中移除指定键的映射,如果存在的话。
     */
    public V remove(Object key) {
        Node<K,V> e;
        return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value; //调用 removeNode 方法,返回 e 的值
    }

    /**
     * 移除节点方法
     * 
     * 实现 Map.remove 和相关方法。
     *
     * matchValue 参数为 true 时代表只在值相等的时候移除
     * movable 参数 false 时代表移除过程中不移除其他节点
     */
    final Node<K,V> removeNode(int hash, Object key, Object value,
                               boolean matchValue, boolean movable) {
        Node<K,V>[] tab; Node<K,V> p; int n, index;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (p = tab[index = (n - 1) & hash]) != null) { //判断 table 不为 null 且 table 元素不为空且 hash 对应的 Node 存在
            Node<K,V> node = null, e; K k; V v;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                node = p; //首节点验证(hash + 键)
            else if ((e = p.next) != null) { //首节点不是参数要移除的节点,判断是否有后续节点
                if (p instanceof TreeNode) //如果 p 是 TreeNode 实例 
                    node = ((TreeNode<K,V>)p).getTreeNode(hash, key); //调用 p.getTreeNode 方法,这个方法还未出现,稍后分析
                else { //如果 p 是普通 Node
                    do {
                        if (e.hash == hash &&
                            ((k = e.key) == key ||
                             (key != null && key.equals(k)))) { //循环比较
                            node = e; //跳出最后一次循环之前,node 被赋值为要删除的 Node,如果不存在要删除的节点,node 为 null
                            break; //得到与参数相同的 Node,跳出循环
                        }
                        p = e; //跳出最后一次循环之前,p 被赋值为要删除的 Node,如果没有找到要删除的节点,p 为 e 的尾节点
                    } while ((e = e.next) != null); //循环条件是当前 Node 还存在后续节点
                }
            }
            if (node != null && (!matchValue || (v = node.value) == value ||
                                 (value != null && value.equals(v)))) { //如果找到了要删除的节点,并且(不相等也可以删除 || 或者必须相等才能删除但是要删除的 node 的值等于指定参数值 || 或者(必须相等才能删除并且指定参数值不为 null 并且 value.equals(v)))
                if (node instanceof TreeNode) //如果 node 是 TreeNode 实例
                    ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable); //调用 node.removeTreeNode 删除树化节点方法,这个方法还未出现,稍后分析
                else if (node == p) //如果 node 只是普通节点,并且 node 与 p 相等,说明至少执行了一次完整的循环(没有 break)
                    tab[index] = node.next; //更新 hash 对应的数组位置,直接将要删除节点的下一个节点放入该位置(这里没动,难道被删除位置的前面的节点都不要了吗。。)
                else //否则只剩下一种情况,一次完整的循环都没有执行完,直接发现 e 就是与参数对应的要删除的 Node,而这时 p 还是这个要删除元素的上一个节点
                    p.next = node.next; //将 p 的下一个节点置为要删除 node 的下一个节点,使 p 与 node 的下一个节点相连
                ++modCount; //结构性变更,自增 modCount
                --size; //自减 size
                afterNodeRemoval(node); //调用 afterNodeRemoval 方法,这个方法还未出现,稍后分析
                return node; //返回要删除的节点
            }
        }
        return null; //table 数组不存在或者 table 没有元素或者不存在 hash 对应的 Node,返回 null
    }

    /**
     * 重要方法,清空方法
     * 
     * 移除当前 map 中所有映射。在调用方法结束后 map 将为空。
     */
    public void clear() {
        Node<K,V>[] tab;
        modCount++; //结构性变更,自增 modCount
        if ((tab = table) != null && size > 0) { //如果 table 数组不为 null 且当前 HashMap 长度大于 0
            size = 0; //当前 HashMap 长度置为 0
            for (int i = 0; i < tab.length; ++i) //循环数组
                tab[i] = null; //对应位置元素置为 null
        }
    }

    /**
     * 重要方法,判存方法(值)
     *
     * 如果当前 map 有映射到指定值的一个或多个键。
     */
    public boolean containsValue(Object value) {
        Node<K,V>[] tab; V v;
        if ((tab = table) != null && size > 0) { //如果 table 数组不为 null 且当前 HashMap 长度大于 0
            for (int i = 0; i < tab.length; ++i) { //循环数组
                for (Node<K,V> e = tab[i]; e != null; e = e.next) { //for 循环数组当前位置 Node
                    if ((v = e.value) == value ||
                        (value != null && value.equals(v))) //如果当前位置节点值等于参数
                        return true; //返回 true
                }
            }
        }
        return false; //table 数组为 null 或者当前 HashMap 长度小于等于 0 或者遍历数组后没有找到存在对应值的节点,都返回 false
    }

		/**
		 * 这里就是 Map 为什么不属于 Collection 家族的原因			
		 * Collection 针对的都是相同范型类型进行操作,而 Map 则是对于两个范型类型键与值分别操作,再加上键 + 值组合成的条目,就相当于对三种内部类型进行操作,这里通过建立与这三种操作对应的 Set/Collection 内部类,来使得这三种内部类型支持 Collection 家族的方法。
		 **/

    /**
     * 重要方法,获取键 set 方法
     * 
     * 返回一个当前 map 中包含的键s Set 视图。set 被 map 支持,所以对于 map 的修改会影响到 set,反之
     * 亦然。如果 map 在一个涵盖 set 的迭代器创建后被修改了(除了通过迭代器自己的 remove 操作),迭代
     * 结果是不可定义的。set 支持移除元素,意味着从 map 中移除了对应的映射,通过 Iterator.remove,
     * Set.remove,removeAll,retainAll 操作。它不支持 add 或者 addAll 操作。AbstractMap 篇中提
     * 到的最重要的方法
     */
    public Set<K> keySet() {
        Set<K> ks = keySet;
        if (ks == null) {
            ks = new KeySet(); //调用空的 KeySet 构造器
            keySet = ks;
        }
        return ks;
    }

		/**
		 * KeySet 类继承自 AbstractSet 抽象类,由于是 HashMap 的内部类,又是 AbstractSet 的实现
		 * 了,KeySet 拥有了 AbstractSet(Collection)的全部特性,以及 HashMap 中的所有实现方式,
		 * 二合一,注意此类中的方法全部为 final
		 **/
    final class KeySet extends AbstractSet<K> {
    		/**重写 AbstractSet,AbstractCollection,Collection 的方法,使用当前 HashMap 的变量与方法实现**/
        public final int size()                 { return size; } //返回当前 HashMap 的长度
        public final void clear()               { HashMap.this.clear(); } //调用 HashMap 当前实例的 clear 方法
        public final Iterator<K> iterator()     { return new KeyIterator(); } //返回一个空的 KeyIterator 构造器,这个类还未出现,稍后分析
        public final boolean contains(Object o) { return containsKey(o); } //实际调用 HashMap 的 containsKey 方法
        public final boolean remove(Object key) {
            return removeNode(hash(key), key, null, false, true) != null; //实际调用 HashMap 的 removeNode 方法
        }
        public final Spliterator<K> spliterator() {
            return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0); //返回一个带当前 HashMap 实例的 KeySpliterator 并行迭代器构造器,这个类还未出现,稍后分析
        }
        public final void forEach(Consumer<? super K> action) { //循环消费
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount; //消费前记录 modCount
                for (int i = 0; i < tab.length; ++i) { //循环 table 数组
                    for (Node<K,V> e = tab[i]; e != null; e = e.next) //循环 Node 链
                        action.accept(e.key); //消费键
                }
                if (modCount != mc) //延时操作后检查 modCount
                    throw new ConcurrentModificationException();
            }
        }
    }

    /**
     * 重要方法,获取值 Collection 方法
     * 
     * 返回一个当前 map 中包含的值s的 {@link Collection} 视图。这个 collection 是被 map 支持的,
     * 所以改变 map 会影响到 collection,反之亦然。如果在一个涵盖 collection 的迭代操作过程中改动了 
     * map(除了通过一个迭代器自己的 remove 操作),迭代操作的结果是不可定义的。map 中的映射,通过 
     * Iterator.remove,Collection.remove,removeAll,retainAll 和 clear 操作。它不支持 add 
     * 或者 addAll 操作。
     */
    public Collection<V> values() {
        Collection<V> vs = values;
        if (vs == null) {
            vs = new Values(); //调用空的 Values 构造器
            values = vs;
        }
        return vs;
    }

	/**
	* Values 类继承自 AbstractCollection 抽象类,由于是 HashMap 的内部类,又是 
	* AbstractCollection 的实现了,Values 拥有了 AbstractCollection(Collection)的全部特性,以
	* 及 HashMap 中的所有实现方式,二合一,注意此类中的方法全部为 final
	**/
    final class Values extends AbstractCollection<V> {
        public final int size()                 { return size; } //与 KeySet 相同
        public final void clear()               { HashMap.this.clear(); } //与 KeySet 相同
        public final Iterator<V> iterator()     { return new ValueIterator(); } //构造空 ValueIterator 对象
        public final boolean contains(Object o) { return containsValue(o); } //调用 containsValue 方法
        public final Spliterator<V> spliterator() { //构造 ValueSpliterator 对象,参数为当前 HashMap 实例
            return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
        public final void forEach(Consumer<? super V> action) { //循环消费
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (int i = 0; i < tab.length; ++i) {
                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
                        action.accept(e.value); //注意这里消费的是值
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
    }

    /**
     * 重要方法,获取 Map.Entry Set 方法
     * 
     * 返回一个当前 map 中包含的一个映射 {@link Set} 视图。这个 set 由 map 支持,所以对于 map 的改
     * 动会影响到 set,反之亦然。如果在一个涵盖 set 的迭代操作过程中 map 被改变了(除了通过迭代器自己的 
     * remove 操作,或者在一个迭代器返回的 map 条目上通过 setValue 操作)迭代操作的结果是不可定义的。
     * set 支持移除元素,相当于从 map 中移除对应的映射,通过 Iterator.remove,Set.remove,
     * removeAll,retainAll 和 clear 操作。它不支持 add 或者 addAll 操作。AbstractMap 中的重要方
     * 法 entrySet
     */
    public Set<Map.Entry<K,V>> entrySet() {
        Set<Map.Entry<K,V>> es;
        return (es = entrySet) == null ? (entrySet = new EntrySet()) : es; //调用 EntrySet 空构造器
    }

	/**
	 * EntrySet 继承自 AbstractSet 抽象类,由于是 HashMap 的内部类,又是 AbstractSet 的实现了,
	 * KeySet 拥有了 AbstractSet(Collection)的全部特性,以及 HashMap 中的所有实现方式,二合一,
	 * 注意此类中的方法全部为 final
	 **/
    final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        public final int size()                 { return size; } //与 KeySet 相同
        public final void clear()               { HashMap.this.clear(); } //与 KeySet 相同
        public final Iterator<Map.Entry<K,V>> iterator() {
            return new EntryIterator(); //调用 EntryIterator 的空构造器
        }
        
        //这里实现就与 KeySet 和 Values 不同了
        public final boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false; //如果参数不是 Map.Entry 实例,返回 false
            Map.Entry<?,?> e = (Map.Entry<?,?>) o;
            Object key = e.getKey(); //缓存参数的键
            Node<K,V> candidate = getNode(hash(key), key); //调用 getNode 方法获得 Node
            return candidate != null && candidate.equals(e); //判断 Node 与参数的相等性
        }
        
        //移除指定参数方法
        public final boolean remove(Object o) {
            if (o instanceof Map.Entry) { //判断参数是否是 Map.Entry 实例
                Map.Entry<?,?> e = (Map.Entry<?,?>) o;
                Object key = e.getKey();
                Object value = e.getValue();
                return removeNode(hash(key), key, value, true, true) != null; //调用 removeNode 方法
            }
            return false;
        }
        
        //获取并行迭代器
        public final Spliterator<Map.Entry<K,V>> spliterator() {
            return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0); //调用 EntrySpliterator 的带参构造器,参数为 HashMap 当前实例
        }
        
        //循环消费
        public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount; //缓存 modCount
                for (int i = 0; i < tab.length; ++i) {
                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
                        action.accept(e); //消费 Node
                }
                if (modCount != mc) //比较操作前后 modCount 是否一致
                    throw new ConcurrentModificationException();
            }
        }
    }

    // 重写 JDK 8 Map 扩展方法

	/**重写 getOrDefault 方法**/
    @Override
    public V getOrDefault(Object key, V defaultValue) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? defaultValue : e.value; //获取 table 数组中 key hash 位置的 Node,没有的话就返回参数 defaultValue
    }

	/**重写 putIfAbsent 方法**/
    @Override
    public V putIfAbsent(K key, V value) {
        return putVal(hash(key), key, value, true, true); //调用 putVal 方法,注意 onlyIfAbsent 与 evict 参数都为 true
    }

	/**重写 remove 方法**/
    @Override
    public boolean remove(Object key, Object value) {
        return removeNode(hash(key), key, value, true, true) != null; //调用 removeNode 方法,注意 matchValue 与 movable 参数都为 true
    }

	/**重写三参 replace 方法**/
    @Override
    public boolean replace(K key, V oldValue, V newValue) {
        Node<K,V> e; V v;
        if ((e = getNode(hash(key), key)) != null && //如果键参数对应的 Node 存在
            ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) { //并且与旧值参数相同
            e.value = newValue; //新值替换旧值
            afterNodeAccess(e); //调用 afterNodeAccess 方法,这个方法还未出现,稍后分析
            return true; //返回 true
        }
        return false; //如果键参数对应的 Node 不存在或者旧值参数与 Node 中原有值不相同,返回 false
    }

	/**重写双参 replace 方法**/
    @Override
    public V replace(K key, V value) {
        Node<K,V> e;
        if ((e = getNode(hash(key), key)) != null) { //判断键参数对应的 Node 存在,不比较旧值
            V oldValue = e.value;
            e.value = value; //直接替换旧值
            afterNodeAccess(e);  //调用 afterNodeAccess 方法
            return oldValue; //返回旧值
        }
        return null; //如果键参数对应的 Node 不存在,返回 null
    }

	/**重写 computeIfAbsent 方法**/
    @Override
    public V computeIfAbsent(K key,
                             Function<? super K, ? extends V> mappingFunction) {
        if (mappingFunction == null)
            throw new NullPointerException();
        int hash = 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) //如果当前 HashMap 长度大于阈值或者 table 数组为 null 或者 table 数组长度为 0
            n = (tab = resize()).length; //重构 
        if ((first = tab[i = (n - 1) & hash]) != null) { //如果 hash 对应的 Node 不为 null
            if (first instanceof TreeNode) //判断该 Node 是否为 TreeNode 实例
                old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key); //调用 first.getTreeNode 方法,此方法稍后分析
            else { //如果只是普通 Node
                Node<K,V> e = first; K k;
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k)))) {
                        old = e; //循环比较直到到找到参数 key 对应的 Node
                        break;
                    }
                    ++binCount; //自增容器累加器
                } while ((e = e.next) != null); 
            }
            V oldValue;
            if (old != null && (oldValue = old.value) != null) { //大部分情况 old 都不会为 null,如果 old 的值也不为 null
                afterNodeAccess(old); //调用 afterNodeAccess 方法
                return oldValue; //返回 oldValue
            }
        }
        //hash 对应的数组位置的 Node 为 null,这里就开始体现 computeIfAbsent 的真正含义了,找不到,才对它作用函数
        V v = mappingFunction.apply(key); //对键使用转换函数
        if (v == null) { 
            return null; //如果转换结果为 null,返回 null
        } else if (old != null) {
            old.value = v; //用转换结果替换 old 的值
            afterNodeAccess(old); //调用 afterNodeAccess 方法
            return v; //返回转换后的值
        }
        else if (t != null) //t 就是树化 hash 对应的 Node 之后的树化节点
            t.putTreeVal(this, tab, hash, key, v); //调用 t.putTreeVal 方法
        else { //树化节点为 null
            tab[i] = newNode(hash, key, v, first);  //调用 newNode 方法创建新节点,塞入 hash 值对应的数组位置
            if (binCount >= TREEIFY_THRESHOLD - 1) //如果 binCount 大于等于树化阈值 - 1
                treeifyBin(tab, hash); //调用树化容器方法
        }
        ++modCount; //结构性变更,自增 modCount
        ++size; //自增 size
        afterNodeInsertion(true); //调用 afterNodeIsInsertion 方法
        return v; //返回 v
    }

	/**重写 computeIfPresent 方法**/
    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;
        int hash = hash(key);
        if ((e = getNode(hash, key)) != null &&
            (oldValue = e.value) != null) { //如果 key 对应的 Node 存在
            V v = remappingFunction.apply(key, oldValue); //直接对其作用函数
            if (v != null) { //如果作用函数结果不为 null
                e.value = v; //结果替换 e 原来的值
                afterNodeAccess(e); //调用 afterNodeAccess 方法
                return v; //返回新值
            }
            else //如果 v 是 null
                removeNode(hash, key, null, false, true); //调用 removeNode 方法
        }
        return null; //如果没有找到 key 对应的 Node,返回 null
    }

	/**重写 compute 方法**/
    @Override
    public V compute(K key,
                     BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        if (remappingFunction == null)
            throw new NullPointerException();
        int hash = 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);
            }
        } //到这里为止,与 computeIfAbsent 方法完全一样,下面开始不同
        V oldValue = (old == null) ? null : old.value; 
        V v = remappingFunction.apply(key, oldValue); //注意 computeIfAbsent 这一步只是调用 Function 作用函数接口抽象方法,只接收一个泛型参数 key,而 compute 方法中调用的是 BiFunciton 作用函数接口抽象方法,接收两个泛型参数,key 和旧值,对它们应用函数作用
        if (old != null) { //这里也与 computeIfAbsent 方法不同,这里先判断 old
            if (v != null) { 
                old.value = v;
                afterNodeAccess(old); //如果调用函数作用后的结果不为 null,结果替换掉 key 对应的 Node 的值,调用 afterNodeAccess 方法
            }
            else
                removeNode(hash, key, null, false, true); //如果调用函数作用后的结果是 null,调用 removeNode 方法
        }
        else if (v != null) { //如果是没有找到对应 Node,也可能是树化后调用 getTreeNode 返回 null,但是函数作用后的结果不为 null
            if (t != null) //说明树化过
                t.putTreeVal(this, tab, hash, key, v); //调用 t.putTreeVal 方法
            else {  //说明没有找到对应 Node
                tab[i] = newNode(hash, key, v, first); //调用 newNode 方法新建 Node 插入 hash 对应的数组位置
                if (binCount >= TREEIFY_THRESHOLD - 1) //如果容器累加器大于等于树化阈值 - 1
                    treeifyBin(tab, hash); //树化容器
            }
            ++modCount; //结构性变更,自增 modCount
            ++size; //自增 size
            afterNodeInsertion(true); //调用 afterNodeInsertion 方法
        }
        return v; //返回 v
    }

	/**重写 merge 方法**/
    @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();
        int hash = 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);
            }
        } //到这里都与 compute 方法相同,下面就开始不一样了
        if (old != null) { //首先判断 old 是否不为空,说明要么是通过树化后通过 getTreeNode 方法获得了正确的结果,要么是遍历普通 Node 获得了结果
            V v;
            if (old.value != null) //如果 old 的值不为 null
                v = remappingFunction.apply(old.value, value); //应用函数调用,注意这里不是用键与旧值了,而是用旧值与参数值
            else
                v = value; //如果 old 的值为 null,使用参数值
            if (v != null) { //参数值之前已经检查过了肯定不会为 null,所以这里检查的就是应用函数调用后结果是否为 null
                old.value = v; //不为 null,v 替换掉 key 对应 Node 的值
                afterNodeAccess(old); //调用 afterNodeAccess 方法
            }
            else
                removeNode(hash, key, null, false, true); //函数调用结果为 null,调用 removeNode 方法
            return v;
        }
        if (value != null) { //这里很奇怪,又对 value 进行了一次非空判断,我理解为在函数调用作用时,对 value 产生了副作用,使其变成变成了 null
            if (t != null) //说明树化过
                t.putTreeVal(this, tab, hash, key, value); //调用 t.putTreeVal 方法
            else { //说明遍历普通 Node 没有获得结果
                tab[i] = newNode(hash, key, value, first); //调用 newNode 方法新建 Node,塞入数组第 i 个位置
                if (binCount >= TREEIFY_THRESHOLD - 1) //如果容器累加器大于等于 TREEIFY_THRESHOLD - 1
                    treeifyBin(tab, hash); //树化容器
            }
            ++modCount; //自增 modCount
            ++size; //自增 size
            afterNodeInsertion(true); //调用 afterNodeInsertion 方法
        }
        return value; //返回 value
    }

	/**重写 forEach 方法**/
    @Override
    public void forEach(BiConsumer<? super K, ? super V> action) {
        Node<K,V>[] tab;
        if (action == null)
            throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
            int mc = modCount; //缓存 modCount
            for (int i = 0; i < tab.length; ++i) { //遍历 Node 数组
                for (Node<K,V> e = tab[i]; e != null; e = e.next) //遍历 Node 链
                    action.accept(e.key, e.value); //消费当前 Node 的键值
            }
            if (modCount != mc) //比较操作前后 modCount 是否相同
                throw new ConcurrentModificationException();
        }
    }

	/**重写 replaceAll 方法**/
    @Override
    public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
        Node<K,V>[] tab;
        if (function == null)
            throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
            int mc = modCount; //缓存 modCount
            for (int i = 0; i < tab.length; ++i) { 
                for (Node<K,V> e = tab[i]; e != null; e = e.next) {
                    e.value = function.apply(e.key, e.value); //函数调用作用当前 Node 的键值,并替换调用当前 Node 的值
                }
            }
            if (modCount != mc) //判断操作前后 modCount 是否相等
                throw new ConcurrentModificationException();
        }
    }

    /* ------------------------------------------------------------ */
    // 克隆和序列化

    /**
     * 返回一个当前 HashMap 实例的浅拷贝:键s和值s不可隆
     */
    @SuppressWarnings("unchecked")
    @Override
    public Object clone() {
        HashMap<K,V> result;
        try {
            result = (HashMap<K,V>)super.clone(); //调用 AbstractMap#clone 方法
        } catch (CloneNotSupportedException e) {
            //这里注意 AbstractMap 其实是没有实现 Cloneable 的,但是 HashMap 实现了,所以应该是不会抛出这个异常的
            throw new InternalError(e);
        }
        result.reinitialize(); //调用 result.reinitialize 方法
        result.putMapEntries(this, false); //调用 result.putMapEntries 方法
        return result; //返回 result
    }

    //这些方法也在序列化 HashSets 的时候用到
    final float loadFactor() { return loadFactor; } //返回当前 HashMap 的加载因子
    final int capacity() { //返回当前 HashMap 的容量
        return (table != null) ? table.length : 
            (threshold > 0) ? threshold :
            DEFAULT_INITIAL_CAPACITY; //当当前 HashMap 的 table 不为 null 时候返回 table 长度,否则再判断当前 HashMap 的阈值是否大于 0,如果是返回阈值,否则返回 DEFAULT_INITIAL_CAPACITY
    }

    /**
     * 序列化读方法
     */
    private void writeObject(java.io.ObjectOutputStream s)
        throws IOException {
        int buckets = capacity(); //调用 capacity 方法获得当前 HashMap 桶的数量
        //写出阈值,加载因子和其他隐藏内容
        s.defaultWriteObject(); 
        s.writeInt(buckets);  //写出桶的数量
        s.writeInt(size);  //写出当前 HashMap 的长度,其实整个 HashMap 中没有任何手动设置 size 的地方(包括构造其中), size 都是通过 putVal,computeIfAbsent,compute,merge 等方法自增的
        internalWriteEntries(s); //调用 internalWriteEntries 方法
    }

    /**
     * 序列化写方法
     */
    private void readObject(java.io.ObjectInputStream s)
        throws IOException, ClassNotFoundException {
        //读入阈值(忽略),加载因子和其他隐藏内容
        s.defaultReadObject();
        reinitialize(); //调用 reinitialize 方法
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new InvalidObjectException("Illegal load factor: " +
                                             loadFactor);
        s.readInt();                // 读取但是忽略桶数量(这在本方法内部使用,不引起其他副作用)
        int mappings = s.readInt(); // 读取映射数量
        if (mappings < 0)
            throw new InvalidObjectException("Illegal mappings count: " +
                                             mappings);
        else if (mappings > 0) { //(如果为 0,使用默认)
            // 只有在给定加载因子在 0.25 到 4.0 之间时才指定 table 的长度
            float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f); //计算加载因子 
            float fc = (float)mappings / lf + 1.0f; //映射数除以加载因子 + 1.0f
            int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
                       DEFAULT_INITIAL_CAPACITY :
                       (fc >= MAXIMUM_CAPACITY) ?
                       MAXIMUM_CAPACITY :
                       tableSizeFor((int)fc)); //不小于 DEFAULT_INITIAL_CAPACITY 也不大于等于 MAXIMUM_CAPACITY 时候调用 tableSizeFor 方法,作为容量,其他情况使用默认值
            float ft = (float)cap * lf;
            threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
                         (int)ft : Integer.MAX_VALUE); //计算阈值

            // 参照 Map.Entry[].class,因为它是与我们真实创建的产物最接近的公共类型。
            SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, cap);
            @SuppressWarnings({"rawtypes","unchecked"})
            // 初始化 Node 数组
            Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
            table = tab;

            //读键s和值s,并放入 HashMap 的映射中
            for (int i = 0; i < mappings; i++) {
                @SuppressWarnings("unchecked")
                    K key = (K) s.readObject(); 
                @SuppressWarnings("unchecked")
                    V value = (V) s.readObject();
                putVal(hash(key), key, value, false, false); //调用 putVal 方法
            }
        }
    }

    /* ------------------------------------------------------------ */
    // 迭代器,注意 HashMap 也有迭代器,但是不实现 Iterator

    abstract class HashIterator {
        Node<K,V> next;        // 下一个要返回的条目
        Node<K,V> current;     // 当前条目
        int expectedModCount;  // 为了 fast-fail
        int index;             // 当前槽,类似于游标(cursor)

        HashIterator() {
            expectedModCount = modCount; //缓存 modCount
            Node<K,V>[] t = table; 
            current = next = null;
            index = 0;
            if (t != null && size > 0) { // 送入第一个条目
                do {} while (index < t.length && (next = t[index++]) == null); //这种做法也很独特,将所有逻辑全部包裹在 while 中,循环判断只要 index 小于当前 HashMap 的长度,并且 t 的 index 位置 Node 为 null,自增 index,也就是说获得数组中第一个不为 null 的元素的位置(这种做法可能是因为 HashMap 是一个散列表,它并不是像 ArrayList 那样是一个连续的数组,所以 ArrayList 的迭代器只需要判断 cursor 是否等于 size 就可以了,散列表是非连续的,所以它要循环直到发现不为 null 的元素或者到达 table 的长度,相当于是移动迭代器游标)
            }
        }
 				
 				//HashIterator 迭代器的 hasNext 方法,对应 Iterator 的 hasNext 方法
        public final boolean hasNext() {
            return next != null; //这里跟 Iterator 不同,Collection 中 Iterator 通常判断直接跟 size 判断,不为 size 就说明游标之后还有值
        }

				//HashIterator 迭代器的 nextNode 方法,对应 Iterator 的 next 方法
        final Node<K,V> nextNode() {
            Node<K,V>[] t;
            Node<K,V> e = next; //缓存下一个条目
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
            if ((next = (current = e).next) == null && (t = table) != null) { //将下一个条目赋值给 current 条目,判断如果下一个条目之后的条目为 null 并且 table 不为 null
                do {} while (index < t.length && (next = t[index++]) == null); //循环自增 index 直到 index < t.length 并且 t 的 index 位置的条目不为 null(移动槽位位置,相当于是移动迭代器游标,这样做的原因是下次调用 nextNode 时候一定有值)
            }
            return e; //返回 e
        }

				//HashIterator 迭代器的 remove 方法,对应 Iterator 的 remove 方法
        public final void remove() { 
            Node<K,V> p = current;
            if (p == null)
                throw new IllegalStateException(); //如果迭代器中当前条目为空,抛出 IllegalStateException
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            current = null; //当前条目置为 null
            K key = p.key; 
            removeNode(hash(key), key, null, false, false); //调用 removeNdoe 方法将当前条目 key 所对应的节点移除
            expectedModCount = modCount; //结构性更改,expectedModCount 更新为 modCount
        }
    }

		//键迭代器,继承自 HashIterator,实现 Iterator 接口,注意它就是 KeySet 的迭代器,它的范型是键类型
    final class KeyIterator extends HashIterator
        implements Iterator<K> {
        //重写 Iterator 的 next 方法,这里就看出来 Iterator 设计与 HashMap 桥接的精妙,我有三种内部数据结构,它们操作的数据是相同的,只是返回数据的部分不同,那么就定义三个分别实现它们范型类型实现的 Iterator
        public final K next() { return nextNode().key; } //调用 nextNode 获得下一个条目,返回条目的键
    }

		//值迭代器,继承自 HashIterator,实现 Iterator 接口,注意它就是 Values 的迭代器,它的范型是值类型
    final class ValueIterator extends HashIterator
        implements Iterator<V> {
        public final V next() { return nextNode().value; } //调用 nextNode 获得下一个条目,返回条目的值
    }

	  //条目迭代器,继承自 HashIterator,实现 Iterator 接口,它的范型是键值条目类型
    final class EntryIterator extends HashIterator
        implements Iterator<Map.Entry<K,V>> {
        public final Map.Entry<K,V> next() { return nextNode(); } //调用 nextNode 获得下一个条目,返回条目
    }

    /* ------------------------------------------------------------ */
    // 并行迭代器

    static class HashMapSpliterator<K,V> {
        final HashMap<K,V> map;
        Node<K,V> current;          // 当前 Node
        int index;                  // 当前游标位置,在前进/分割的时候修改
        int fence;                  // 一个过去的最终位置(栅栏)
        int est;                    // 长度估值
        int expectedModCount;       // 为了并行修改检查

        HashMapSpliterator(HashMap<K,V> m, int origin,
                           int fence, int est,
                           int expectedModCount) {
            this.map = m;
            this.index = origin;
            this.fence = fence;
            this.est = est;
            this.expectedModCount = expectedModCount;
        }

				//获取当前并行迭代器栅栏
        final int getFence() { // 在第一次使用时初始化长度和栅栏
            int hi;
            if ((hi = fence) < 0) { //栅栏小于 0 的时候
                HashMap<K,V> m = map;
                est = m.size;
                expectedModCount = m.modCount;
                Node<K,V>[] tab = m.table;
                hi = fence = (tab == null) ? 0 : tab.length;
            }
            return hi;
        }

        public final long estimateSize() { //估算长度
            getFence(); // 强制初始化
            return (long) est; //返回 est
        }
    }

	//键并行迭代器,继承自 HashMapSpliterator,实现了 Spliterator 接口
    static final class KeySpliterator<K,V>
        extends HashMapSpliterator<K,V>
        implements Spliterator<K> {
        KeySpliterator(HashMap<K,V> m, int origin, int fence, int est,
                       int expectedModCount) {
            super(m, origin, fence, est, expectedModCount);
        }

		/**尝试分隔方法,还是使用二分法**/
        public KeySpliterator<K,V> trySplit() {
            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
            return (lo >= mid || current != null) ? null :
                new KeySpliterator<>(map, lo, index = mid, est >>>= 1,
                                        expectedModCount);
        }

		/**消费剩余元素方法**/
        public void forEachRemaining(Consumer<? super K> action) {
            int i, hi, mc;
            if (action == null)
                throw new NullPointerException();
            HashMap<K,V> m = map;
            Node<K,V>[] tab = m.table;
            if ((hi = fence) < 0) { //如果栅栏小于 0
                mc = expectedModCount = m.modCount; //缓存当前 HashMap 的 modCount
                hi = fence = (tab == null) ? 0 : tab.length; //缓存 table 长度
            }
            else //否则
                mc = expectedModCount; //缓存当前 KeySpliterator 的 expectedModCount
            if (tab != null && tab.length >= hi &&
                (i = index) >= 0 && (i < (index = hi) || current != null)) {
                Node<K,V> p = current; //缓存 current
                current = null; //当前 KeySpliterator 的 current 置为 null
                do {
                    if (p == null) //不存在更多的 Node 了
                        p = tab[i++]; //步移 i,查找其他散列元素 
                    else {
                        action.accept(p.key); //消费 p 的键
                        p = p.next; //步移
                    }
                } while (p != null || i < hi); //循环条件 p 不为 null 并且 i 小于 hi 
                if (m.modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }

		/**进一步尝试方法**/
        public boolean tryAdvance(Consumer<? super K> action) {
            int hi;
            if (action == null)
                throw new NullPointerException();
            Node<K,V>[] tab = map.table;
            if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
                while (current != null || index < hi) {
                    if (current == null) //如果 current 为 null
                        current = tab[index++]; //自增 index 直到 current 不为 null,其实就是获取初始 index 之后第一个数据
                    else {
                        K k = current.key; 
                        current = current.next; //步移 current
                        action.accept(k); //消费 current 的键
                        if (map.modCount != expectedModCount) 
                            throw new ConcurrentModificationException();
                        return true; //返回 true
                    }
                }
            }
            return false; //返回 false,其实就是初始 index 后没有任何数据了
        }

		/**特征值方法**/
        public int characteristics() {
            return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
                Spliterator.DISTINCT;
        }
    }

	//值并行迭代器,继承自 HashMapSpliterator,实现了 Spliterator
    static final class ValueSpliterator<K,V>
        extends HashMapSpliterator<K,V>
        implements Spliterator<V> {
        ValueSpliterator(HashMap<K,V> m, int origin, int fence, int est,
                         int expectedModCount) {
            //构造方法与 KeySpliterator 相同             
            super(m, origin, fence, est, expectedModCount);
        }

        public ValueSpliterator<K,V> trySplit() {
            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
            return (lo >= mid || current != null) ? null :
                new ValueSpliterator<>(map, lo, index = mid, est >>>= 1,
                                          expectedModCount);
        }

        public void forEachRemaining(Consumer<? super V> action) {
            int i, hi, mc;
            if (action == null)
                throw new NullPointerException();
            HashMap<K,V> m = map;
            Node<K,V>[] tab = m.table;
            if ((hi = fence) < 0) {
                mc = expectedModCount = m.modCount;
                hi = fence = (tab == null) ? 0 : tab.length;
            }
            else
                mc = expectedModCount;
            if (tab != null && tab.length >= hi &&
                (i = index) >= 0 && (i < (index = hi) || current != null)) {
                Node<K,V> p = current;
                current = null;
                do {
                    if (p == null)
                        p = tab[i++];
                    else {
                        action.accept(p.value); //可以看到 ValueSliterator 与 KeySpliterator 其实是完全一样的,只是这里消费的是值
                        p = p.next;
                    }
                } while (p != null || i < hi);
                if (m.modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }

        public boolean tryAdvance(Consumer<? super V> action) {
            int hi;
            if (action == null)
                throw new NullPointerException();
            Node<K,V>[] tab = map.table;
            if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
                while (current != null || index < hi) {
                    if (current == null)
                        current = tab[index++];
                    else {
                        V v = current.value;
                        current = current.next;
                        action.accept(v); //消费值
                        if (map.modCount != expectedModCount)
                            throw new ConcurrentModificationException();
                        return true;
                    }
                }
            }
            return false;
        }

        public int characteristics() {
            return (fence < 0 || est == map.size ? Spliterator.SIZED : 0);
        }
    }

    static final class EntrySpliterator<K,V>
        extends HashMapSpliterator<K,V>
        implements Spliterator<Map.Entry<K,V>> {
        EntrySpliterator(HashMap<K,V> m, int origin, int fence, int est,
                         int expectedModCount) {
            super(m, origin, fence, est, expectedModCount);
        }

        public EntrySpliterator<K,V> trySplit() {
            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
            return (lo >= mid || current != null) ? null :
                new EntrySpliterator<>(map, lo, index = mid, est >>>= 1,
                                          expectedModCount);
        }

        public void forEachRemaining(Consumer<? super Map.Entry<K,V>> action) {
            int i, hi, mc;
            if (action == null)
                throw new NullPointerException();
            HashMap<K,V> m = map;
            Node<K,V>[] tab = m.table;
            if ((hi = fence) < 0) {
                mc = expectedModCount = m.modCount;
                hi = fence = (tab == null) ? 0 : tab.length;
            }
            else
                mc = expectedModCount;
            if (tab != null && tab.length >= hi &&
                (i = index) >= 0 && (i < (index = hi) || current != null)) {
                Node<K,V> p = current;
                current = null;
                do {
                    if (p == null)
                        p = tab[i++];
                    else {
                        action.accept(p); //消费条目
                        p = p.next;
                    }
                } while (p != null || i < hi);
                if (m.modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }

        public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {
            int hi;
            if (action == null)
                throw new NullPointerException();
            Node<K,V>[] tab = map.table;
            if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
                while (current != null || index < hi) {
                    if (current == null)
                        current = tab[index++];
                    else {
                        Node<K,V> e = current;
                        current = current.next;
                        action.accept(e); //消费条目
                        if (map.modCount != expectedModCount)
                            throw new ConcurrentModificationException();
                        return true;
                    }
                }
            }
            return false;
        }

        public int characteristics() {
            return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
                Spliterator.DISTINCT;
        }
    }

    /* ------------------------------------------------------------ */
    // LinkedHashMap 支持


    /**
     * 一下包保护方法是设计用来被 LinkedHashMap 重写的,不是给任何其他子类。差不多其他内部方法也是包保
     * 护户的但是被定义成了 final,所以可以被 LinkedHashMap,视图类和 HashSet 使用。
     **/

    // 建立一个普通的(非树化)Node 方法
    Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
        return new Node<>(hash, key, value, next);
    }

    // 为了从树化 Nodes 到普通 Nodes 的转化方法
    Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
        return new Node<>(p.hash, p.key, p.value, next);
    }

    // 建立一个树 node
    TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
        return new TreeNode<>(hash, key, value, next);
    }

    // 树化容器
    TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
        return new TreeNode<>(p.hash, p.key, p.value, next);
    }

    /**
     * 重置为默认状态。被 clone 和 readObject 调用。
     */
    void reinitialize() {
        table = null;
        entrySet = null;
        keySet = null;
        values = null;
        modCount = 0;
        threshold = 0;
        size = 0;
    }

    // 允许 LinkedHashMap 放入操作的回调方法
    // afterNodeAccess(这几个方法都是 HashMap 中非常重要的方法,在对 Node 进行操作的时候都会调用,HashMap 中却没有提供实现,那显然之前看到的地方其实在 HashMap 中就是忽略的,但是子类可以自己添加实现)
    // 在对 Node 中的值进行替换后调用
    void afterNodeAccess(Node<K,V> p) { }
    // 在插入新的 Node 后调用
    void afterNodeInsertion(boolean evict) { }
    // 在移除 Node 后调用
    void afterNodeRemoval(Node<K,V> p) { }

    // 在写入对象时候被调用,来保证兼容的顺序
    void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
        Node<K,V>[] tab;
        if (size > 0 && (tab = table) != null) {
            for (int i = 0; i < tab.length; ++i) { //循环 table
                for (Node<K,V> e = tab[i]; e != null; e = e.next) { //循环 node
                    s.writeObject(e.key); //写入键
                    s.writeObject(e.value); //写入值
                }
            }
        }
    }

    /* ------------------------------------------------------------ */
    // 树容器,树化的实现

    /**
     * 树容器的条目。继承自 LinkedHashMap.Entry(反过来继承自 Node)所以可以被普通或者链接 node 当做
     * 扩展使用。这里暂时还没有到分析 LinkedHashMap,所以只需要知道 LinkedHashMap.Entry 继承自 
     * HashMap.Node,所以在 HashMap 中也可以使用就可以了
     */
    static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
        TreeNode<K,V> parent;  //红黑树链接
        TreeNode<K,V> left; //左子树
        TreeNode<K,V> right; //右子树
        TreeNode<K,V> prev;    //前节点,这里保留并扩展了 Node 的概念,删除时需要取消下一个链接
        boolean red; //标记当前 TreeNode 是否为红节点
        //树化节点构造方法
        TreeNode(int hash, K key, V val, Node<K,V> next) {
            super(hash, key, val, next);
        }

        /**
         * 返回包含当前 node 的树根方法
         */
        final TreeNode<K,V> root() {
            for (TreeNode<K,V> r = this, p;;) {
                if ((p = r.parent) == null) //向父节点遍历,知道父节点为 null
                    return r; //返回当前遍历到的节点
                r = p; //步移
            }
        }

        /**
         * 保证指定根是它的容器的第一个 node,改变 Node 链与根/子树 关系。
         */
        static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
            int n;
            if (root != null && tab != null && (n = tab.length) > 0) { 
                int index = (n - 1) & root.hash; //root.hash 是在 TreeNode 构造方法中传入的,调用父类(Entry 的构造方法,hash 值就是键的 hash)这里其实还是找到键的 hash 在数组中的位置
                TreeNode<K,V> first = (TreeNode<K,V>)tab[index]; //这里缓存了 table 中对应位置的 TreeNode
                if (root != first) { //如果 root 参数不是对应位置的 TreeNode
                    Node<K,V> rn;
                    tab[index] = root; //这里用 root 替换了 table 中对应位置的 TreeNode
                    TreeNode<K,V> rp = root.prev; //缓存上一个节点
                    if ((rn = root.next) != null) //将 root 的下一个节点置为它的右子树
                        ((TreeNode<K,V>)rn).prev = rp; //将 root 的上一个节点置为右子树的上一个节点,注意这里 rp 可能为 null,那么右子树的上一个节点就是 null 了
                    if (rp != null) //如果右子树的上一个节点不为空
                        rp.next = rn; //将右子树的上一个节点的下一个节点置为 rn
                    if (first != null) //如果数组对应位置原来有 TreeNode
                        first.prev = root; //将 root 置为原来 TreeNode 的上一个节点
                    root.next = first; //将原来节点置为 root 的下一个节点
                    root.prev = null; //将 root 的上一个节点置为 null
                }
                assert checkInvariants(root); //调用 checkInvariants 方法,进行断言
            }
        }

        /**
         * 找到 root p 开始的指定 hash 和键的 node。kc 参数在第一次使用 comparing 键s时缓存了 
         * comparableCallFor(key)。这是一个递归方法。
         */
        final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
            TreeNode<K,V> p = this; //缓存当前 TreeNode
            do {
                int ph, dir; K pk;
                TreeNode<K,V> pl = p.left, pr = p.right, q; //缓存左右子树
                //通过比较当前 TreeNode 的 hash 与参数来选择旋转方式
                if ((ph = p.hash) > h) //参数小于当前 TreeNode 的 hash
                    p = pl; //步移,p 置为左子树
                else if (ph < h) //参数大于当前 TreeNode 的 hash
                    p = pr; //步移,p 置为右子树
                else if ((pk = p.key) == k || (k != null && k.equals(pk))) //如果参数 p 的键等于参数
                    return p; //找到了,返回 p
                else if (pl == null) //如果左子树为 null
                    p = pr; //步移,p 置为右子树
                else if (pr == null) //如果右子树也为 null
                    p = pl; //步移,p 置为左子树
                else if ((kc != null || //如果 kc 不为 null 或者
                          (kc = comparableClassFor(k)) != null) && //k 的范型类型是可以可以比较的,将 k 的范型类型缓存给 kc 并且
                         (dir = compareComparables(kc, k, pk)) != 0) //将 k 与 pk 进行自己类型 compareTo 方法的比较,将比较结果赋值给 dir
                    p = (dir < 0) ? pl : pr; //根据 dir 确定是将 p 置为左子树还是右子树
                else if ((q = pr.find(h, k, kc)) != null) //以上均不符合,递归调用 find 方法,h,k 不变,可能出现的情况是 kc 在第一次调用时候为 null,一次分支走完过后 kc 不为 null 了,但是 dir == 0,说明 p 的键为 null 或者 p 的类不为 kc,如果这次递归结果不为 null
                    return q; //返回 q
                else //其他情况
                    p = pl; //步移,p 置为左子树
            } while (p != null); //循环条件,p 不等于 null 时候
            return null; //如果 p 为 null,返回 null
        }

        /**
         * 为 root node 的 find 方法
         */
        final TreeNode<K,V> getTreeNode(int h, Object k) {
            return ((parent != null) ? root() : this).find(h, k, null);
        }

        /**
         * 当出现 hashCode 与不可比较时对于插入元素排序(解决碰撞)工具方法,我们不在乎需要一个完整的顺
         * 序,只是用于维护旋转过程中等价性的一个一致的插入规则(所以 HashMap 说自己不保证顺序是一直不
         * 变的)。解决碰撞比必要的更容易测试一点。
         */
        static int tieBreakOrder(Object a, Object b) {
            int d;
            if (a == null || b == null ||
                (d = a.getClass().getName().
                 compareTo(b.getClass().getName())) == 0) //这里就是说明 a 和 b 是不可比较的
                 // System.identityHashCode 是一个无论对象类有没有实现 hashCode 方法都会返回一个与 hashCode 方法同样返回的 native 方法
                d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
                     -1 : 1);
            return d; //返回 hashCode 比较结果
        }

        /**
         * 树化 Node 方法
         * 
         * 整理当前 node 链接的树节点。
         */
        final 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; //步移到下一个 TreeNode
                x.left = x.right = null; //x 的左右子树都置为 null
                if (root == null) { //如果 root 为 null,第一次会进入这个分支
                    x.parent = null; //x 的父节点置为 null
                    x.red = false; //x 置为黑色节点
                    root = x; //x 赋值给 root,也就是以当前节点作为 root
                }
                else { //root 不为 null
                    K k = x.key;
                    int h = x.hash;
                    Class<?> kc = null;
                    for (TreeNode<K,V> p = root;;) { //遍历根为 root 的 TreeNode 树
                        int dir, ph;
                        K pk = p.key;
                        //确认 dir 的值
                        if ((ph = p.hash) > h)
                            dir = -1;
                        else if (ph < h)
                            dir = 1;
                        else if ((kc == null &&
                                  (kc = comparableClassFor(k)) == null) ||
                                 (dir = compareComparables(kc, k, pk)) == 0)
                            dir = tieBreakOrder(k, pk);
												//p 为当前 root,根据 dir 进行旋转
                        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 = balanceInsertion(root, x); //调用 balanceInsertion 方法,更新 root
                            break;
                        }
                    }
                }
            }
            moveRootToFront(tab, root); //调用 moveRootToFront 方法
        }

        /**
         * 种化方法
         * 
         * 返回一个非树化节点的列表,替换从当前 node 链接的那些 nodes。
         */
        final Node<K,V> untreeify(HashMap<K,V> map) {
            Node<K,V> hd = null, tl = null;
            for (Node<K,V> q = this; q != null; q = q.next) { //遍历当前 TreeNode 的树,步移方式为找到后节点
                Node<K,V> p = map.replacementNode(q, null); //调用 replacementNode 方法种化树化节点
                if (tl == null) //第一次进入循环进入此分支
                    hd = p; //将 p 赋值给 hd(Node 链头 Node)
                else //之后循环进入此分支
                    tl.next = p; //建立 Node 链链接
                tl = p;
            }
            return hd; //返回链头 Node
        }

        /**
         * putVal 方法的树化版本
         *
         * 这个方法只负责找到/新增 TreeNode 和旋转,真正对于获取到的 TreeNode 的值替换还是在 putVal 
         * 方法中完成的
         */
        final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
                                       int h, K k, V v) {
            Class<?> kc = null;
            boolean searched = false;
            TreeNode<K,V> root = (parent != null) ? root() : this; //若存在父树,则获取根,否则 root 就是当前 TreeNode
            for (TreeNode<K,V> p = root;;) { //遍历 root
                int dir, ph; K pk;
                //根据 dir 进行旋转
                if ((ph = p.hash) > h)
                    dir = -1;
                else if (ph < h)
                    dir = 1;
                else if ((pk = p.key) == k || (k != null && k.equals(pk))) //当前 TreeNode 的键与参数相同
                    return p; //直接返回 p
                else if ((kc == null &&
                          (kc = comparableClassFor(k)) == null) ||
                         (dir = compareComparables(kc, k, pk)) == 0) {
                    if (!searched) { //第一次进入循环,必然会进入分支
                        TreeNode<K,V> q, ch;
                        searched = true; //这里置为 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; //如果返回值不为 null,直接返回 q
                    }
                    dir = tieBreakOrder(k, pk); //可能发生了 hash 碰撞,调用 tieBreakOrder 方法
                }

                TreeNode<K,V> xp = p;
                //根据 dir 来循转
                if ((p = (dir <= 0) ? p.left : p.right) == null) {
                    Node<K,V> xpn = xp.next;
                    TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn); //构造新的 TreeNode
                    if (dir <= 0)
                        xp.left = x;
                    else
                        xp.right = x;
                    xp.next = x;
                    x.parent = x.prev = xp;
                    if (xpn != null)
                        ((TreeNode<K,V>)xpn).prev = x;
                    moveRootToFront(tab, balanceInsertion(root, x)); //调用 moveRootToFront 方法
                    return null; //返回 null
                }
            }
        }

        /**
         * 移除指定 node 方法
         *
         * 它必须在调用这个方法之前存在。这比典型的红黑删除代码要混乱,因为我们不能交换一个内部节点与叶子
         * 节点那些可以横向独立访问的钉上“next”指针的后继内容。所以我们用交换树链接来替换。如果当前树看
         * 上去有非常少的节点,(树化)容器被转化为一个普通容器。(最好的触发器在 2 和 6 个 nodes 中
         * 间,主要看树的结构)。
         */
        final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
                                  boolean movable) {
            int n;
            if (tab == null || (n = tab.length) == 0)
                return;
            int index = (n - 1) & hash; //找到当前 node 对应的数组位置
            TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl; //缓存数组对应位置的 TreeNode 作为根
            TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev; //缓存当前 TreeNode 的后节点与前节点
            if (pred == null) //如果前节点为 null
                tab[index] = first = succ; //后节点赋值给 first,更新数组对应位置
            else //前节点不为 null
                pred.next = succ; //更改链接关系
            if (succ != null) //如果后节点不为 null
                succ.prev = pred; //更改链接关系
            if (first == null) //如果数组对应位置本来就为 null 或者前节点与后节点都为 null
                return; //没有东西可以删除,没有链接关系可以更新,直接返回
            if (root.parent != null) //如果 root 存在父树
                root = root.root(); //更新 root 为根
            if (root == null //如果根为 null
                || (movable //可移动的标志 并且
                    && (root.right == null //根右子树为 null
                        || (rl = root.left) == null //或者根左子树为 null
                        || rl.left == null))) { //或者根左子树的左子树为 null
                tab[index] = first.untreeify(map);  // 根不存在或者树太小了,调用 untreeify 方法,种化当前位置的 TreeNode
                return; //返回
            }
            TreeNode<K,V> p = this, pl = left, pr = right, replacement;
            if (pl != null && pr != null) {
                TreeNode<K,V> s = pr, sl;
                while ((sl = s.left) != null) // 查找后继内容,只要左子树不为空就一直找下去,找到左子树为空的父树为止
                    s = sl;
                boolean c = s.red; s.red = p.red; p.red = c; // 转换颜色
                TreeNode<K,V> sr = s.right;
                TreeNode<K,V> pp = p.parent;
                if (s == pr) { // p 是 s 的直接父树
                    p.parent = s;
                    s.right = p;
                }
                else {
                    TreeNode<K,V> sp = s.parent;
                    if ((p.parent = sp) != null) {
                        if (s == sp.left)
                            sp.left = p;
                        else
                            sp.right = p;
                    }
                    if ((s.right = pr) != null)
                        pr.parent = s;
                }
                p.left = null;
                if ((p.right = sr) != null)
                    sr.parent = p;
                if ((s.left = pl) != null)
                    pl.parent = s;
                if ((s.parent = pp) == null)
                    root = s;
                else if (p == pp.left)
                    pp.left = s;
                else
                    pp.right = s;
                if (sr != null)
                    replacement = sr;
                else
                    replacement = p;
            }
            else if (pl != null)
                replacement = pl;
            else if (pr != null)
                replacement = pr;
            else
                replacement = p;
            if (replacement != p) {
                TreeNode<K,V> pp = replacement.parent = p.parent;
                if (pp == null)
                    root = replacement;
                else if (p == pp.left)
                    pp.left = replacement;
                else
                    pp.right = replacement;
                p.left = p.right = p.parent = null;
            }

            TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);

            if (replacement == p) {  // 拆开
                TreeNode<K,V> pp = p.parent;
                p.parent = null;
                if (pp != null) {
                    if (p == pp.left)
                        pp.left = null;
                    else if (p == pp.right)
                        pp.right = null;
                }
            }
            if (movable)
                moveRootToFront(tab, r);
        }

        /**
         * 分割一个数中的 nodes 到更高或者更低的数容器中,或者如果它太小的话,种化它。只能从 resize 方
         * 法调用,查看上面对于分割为和索引的讨论。
         */
        final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
            TreeNode<K,V> b = this;
            // 重新连接 lo 和 hi 链表,保留顺序
            TreeNode<K,V> loHead = null, loTail = null;
            TreeNode<K,V> hiHead = null, hiTail = null;
            int lc = 0, hc = 0;
            for (TreeNode<K,V> e = b, next; e != null; e = next) {
                next = (TreeNode<K,V>)e.next;
                e.next = null;
                if ((e.hash & bit) == 0) {
                    if ((e.prev = loTail) == null)
                        loHead = e;
                    else
                        loTail.next = e;
                    loTail = e;
                    ++lc;
                }
                else {
                    if ((e.prev = hiTail) == null)
                        hiHead = e;
                    else
                        hiTail.next = e;
                    hiTail = e;
                    ++hc;
                }
            }

            if (loHead != null) {
                if (lc <= UNTREEIFY_THRESHOLD)
                    tab[index] = loHead.untreeify(map);
                else {
                    tab[index] = loHead;
                    if (hiHead != null) // (else 的情况是已经被树化了)
                        loHead.treeify(tab);
                }
            }
            if (hiHead != null) {
                if (hc <= UNTREEIFY_THRESHOLD)
                    tab[index + bit] = hiHead.untreeify(map);
                else {
                    tab[index + bit] = hiHead;
                    if (loHead != null)
                        hiHead.treeify(tab);
                }
            }
        }

        /* ------------------------------------------------------------ */
        // 红黑树方法,全部改编自 CLR(这里的 CLR 应该是 .NET Framework 的公共运行时)
		//旋转左子树方法
        static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                              TreeNode<K,V> p) {
            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) {
            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> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                                    TreeNode<K,V> x) {
            x.red = true;
            for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
                if ((xp = x.parent) == null) {
                    x.red = false;
                    return x;
                }
                else if (!xp.red || (xpp = xp.parent) == null)
                    return root;
                if (xp == (xppl = xpp.left)) {
                    if ((xppr = xpp.right) != null && xppr.red) {
                        xppr.red = false;
                        xp.red = false;
                        xpp.red = true;
                        x = xpp;
                    }
                    else {
                        if (x == xp.right) {
                            root = rotateLeft(root, x = xp);
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        if (xp != null) {
                            xp.red = false;
                            if (xpp != null) {
                                xpp.red = true;
                                root = rotateRight(root, xpp);
                            }
                        }
                    }
                }
                else {
                    if (xppl != null && xppl.red) {
                        xppl.red = false;
                        xp.red = false;
                        xpp.red = true;
                        x = xpp;
                    }
                    else {
                        if (x == xp.left) {
                            root = rotateRight(root, x = xp);
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        if (xp != null) {
                            xp.red = false;
                            if (xpp != null) {
                                xpp.red = true;
                                root = rotateLeft(root, xpp);
                            }
                        }
                    }
                }
            }
        }

		//删除后平衡化方法
        static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
                                                   TreeNode<K,V> x) {
            for (TreeNode<K,V> xp, xpl, xpr;;) {
                if (x == null || x == root)
                    return root;
                else if ((xp = x.parent) == null) {
                    x.red = false;
                    return x;
                }
                else if (x.red) {
                    x.red = false;
                    return root;
                }
                else if ((xpl = xp.left) == x) {
                    if ((xpr = xp.right) != null && xpr.red) {
                        xpr.red = false;
                        xp.red = true;
                        root = rotateLeft(root, xp);
                        xpr = (xp = x.parent) == null ? null : xp.right;
                    }
                    if (xpr == null)
                        x = xp;
                    else {
                        TreeNode<K,V> sl = xpr.left, sr = xpr.right;
                        if ((sr == null || !sr.red) &&
                            (sl == null || !sl.red)) {
                            xpr.red = true;
                            x = xp;
                        }
                        else {
                            if (sr == null || !sr.red) {
                                if (sl != null)
                                    sl.red = false;
                                xpr.red = true;
                                root = rotateRight(root, xpr);
                                xpr = (xp = x.parent) == null ?
                                    null : xp.right;
                            }
                            if (xpr != null) {
                                xpr.red = (xp == null) ? false : xp.red;
                                if ((sr = xpr.right) != null)
                                    sr.red = false;
                            }
                            if (xp != null) {
                                xp.red = false;
                                root = rotateLeft(root, xp);
                            }
                            x = root;
                        }
                    }
                }
                else { // 对称的
                    if (xpl != null && xpl.red) {
                        xpl.red = false;
                        xp.red = true;
                        root = rotateRight(root, xp);
                        xpl = (xp = x.parent) == null ? null : xp.left;
                    }
                    if (xpl == null)
                        x = xp;
                    else {
                        TreeNode<K,V> sl = xpl.left, sr = xpl.right;
                        if ((sl == null || !sl.red) &&
                            (sr == null || !sr.red)) {
                            xpl.red = true;
                            x = xp;
                        }
                        else {
                            if (sl == null || !sl.red) {
                                if (sr != null)
                                    sr.red = false;
                                xpl.red = true;
                                root = rotateLeft(root, xpl);
                                xpl = (xp = x.parent) == null ?
                                    null : xp.left;
                            }
                            if (xpl != null) {
                                xpl.red = (xp == null) ? false : xp.red;
                                if ((sl = xpl.left) != null)
                                    sl.red = false;
                            }
                            if (xp != null) {
                                xp.red = false;
                                root = rotateRight(root, xp);
                            }
                            x = root;
                        }
                    }
                }
            }
        }

        /**
         * 地柜不变量检查方法
         */
        static <K,V> boolean checkInvariants(TreeNode<K,V> t) {
            TreeNode<K,V> tp = t.parent, tl = t.left, tr = t.right,
                tb = t.prev, tn = (TreeNode<K,V>)t.next;
            if (tb != null && tb.next != t)
                return false;
            if (tn != null && tn.prev != t)
                return false;
            if (tp != null && t != tp.left && t != tp.right)
                return false;
            if (tl != null && (tl.parent != t || tl.hash > t.hash))
                return false;
            if (tr != null && (tr.parent != t || tr.hash < t.hash))
                return false;
            if (t.red && tl != null && tl.red && tr != null && tr.red)
                return false;
            if (tl != null && !checkInvariants(tl))
                return false;
            if (tr != null && !checkInvariants(tr))
                return false;
            return true;
        }
    }

}

从 HashMap 的源码中,主要可以看到偏向于应用性质的以下几点:
1、HashMap 中所谓的桶其实就是数组的一个位置,由于这个位置可以只存放一个条目,或者一个链式条目,或者一个红黑树条目,所以用桶来比喻它。

2、因为 Map 不是属于集合的数据结构(本身与 Colleciton 没有任何关系),所以对于基于条目返回的键集合(KeySet 内部类)、值集合(Values 内部类)、以及条目的集合(EntrySet 内部类)都被成为视图。对于视图的操作与直接对 Map 进行操作是相互影响的。

3、在出现 Hash 冲突的时候通过对象类的 compareTo(或者 native 的 hashCode 比较方法)来解决

4、所以的通过 Key 查询,其实是通过 Key 的 hash 找到 Node 数组中对应的 Node 链中的 Node(可能为 TreeNode)

5、HashMap 中多次将 (length - 1) & hash 位置的 Node 赋给方法局部变量 first,说明它与迭代器有类似的特性,都可以从指定位置(通过 for 遍历 Node 数组,通过 for 遍历 Node 链表,得到对应 Node)开始取后面的节点,作者将其在方法中重命名为 first,是比较贴切的

6、其实 HashMap 最底层维护的也是一个数组,但是与其他 Collectin 实现类不同的是,这个数组中的元素不是连续的(通过 hash 计算位置,这就是所谓的散列),所以游标位置的确定为只要在不大于数组长度的情况下,不断循环直到找到 Node 为止

7、在迭代过程中,对于元素移除的操作要通过它要移除的属性的对应迭代器来操作(键对应 KeyIterator,值对应 ValueIterator,条目对应 EntrySetIterator),否则会报出在迭代 Collection 最终实现类过程中直接调用 移除方法一样的异常。

8、并行迭代器的初始化其实是发生在 getFence 方法中的。

9、可以看到由于 HashMap 是由两种两种泛型组合成的自典型结构,通过内部类提供了针对键、值、键值组合(条目)的三种串行迭代器与三种并行迭代器,其实每个迭代器数据结构中的迭代器是完全相同的,包括并行的实现,只是 HashMap 中从迭代器中获得的(next)和消费的都是各自对应的键、值以及组合罢了。

10、树化概念(树化容器,树化节点)

11、HashMap 中使用了三种结构来维护条目(数组(hash 位置),链(相同 hash)与红黑树(大链)),所以,不为对于红黑树的操作也会影响到链(比如树化节点的 moveRightToFront,split 等)

而偏向于实现性质的 HashMap 中最重要的树化概念部分,我实在是能力有限,时间成本也不够,没有写的很清楚。

以上就是对于 Map 接口最终实现类 HashMap 的介绍与分析,下一篇将对 LinkedHashMap 进行介绍与分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值