jdk 源码系列之HashMap

{"type":"doc","content":[{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"jdk 源码系列之 HashMap"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"JDK 源码系列"},{"type":"link","attrs":{"href":"https://blog.sincehub.cn/2020/10/31/jdk-HashMap/#jdk-%E6%BA%90%E7%A0%81%E7%B3%BB%E5%88%97","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://blog.sincehub.cn/2020/09/29/jdk-StringBuilder-StringBuffer/","title":null},"content":[{"type":"text","text":"jdk 源码系列之StringBuilder、StringBuffer"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"前言"},{"type":"link","attrs":{"href":"https://blog.sincehub.cn/2020/10/31/jdk-HashMap/#%E5%89%8D%E8%A8%80","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"了解 HashMap,理解扩容机制、数据结构、阈值以及初始化机制,对使用、优化等有所裨益。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"继承关系"},{"type":"link","attrs":{"href":"https://blog.sincehub.cn/2020/10/31/jdk-HashMap/#%E7%BB%A7%E6%89%BF%E5%85%B3%E7%B3%BB","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"1\n2\n3\npublic class HashMap extends AbstractMap\n implements Map, Cloneable, Serializable {\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"从这段代码,我们可知,HashMap"},{"type":"text","text":" 继承 "},{"type":"text","marks":[{"type":"strong"}],"text":"AbstractMap"},{"type":"text","text":" 同时实现 "},{"type":"text","marks":[{"type":"strong"}],"text":"Map"},{"type":"text","text":" 类。换句话说,"},{"type":"text","marks":[{"type":"strong"}],"text":"Map"},{"type":"text","text":" 作为顶级父类,只定义方法,实现由子类实现,还继承 "},{"type":"text","marks":[{"type":"strong"}],"text":"AbstractMap"},{"type":"text","text":" 那也是说,也可以调用 "},{"type":"text","marks":[{"type":"strong"}],"text":"AbstractMap"},{"type":"text","text":" 里面的方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里先摁住,还不清楚 "},{"type":"text","marks":[{"type":"strong"}],"text":"HashMap"},{"type":"text","text":" 调了 "},{"type":"text","marks":[{"type":"strong"}],"text":"AbstractMap"},{"type":"text","text":" 里面哪些方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"HashMap"},{"type":"link","attrs":{"href":"https://blog.sincehub.cn/2020/10/31/jdk-HashMap/#hashmap","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"点进去先看看是数据结构是什么。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"数据结构"},{"type":"link","attrs":{"href":"https://blog.sincehub.cn/2020/10/31/jdk-HashMap/#%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\n/**\n * Basic hash bin node, used for most entries. (See below for\n * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)\n */\nstatic class Node implements Map.Entry {\n final int hash;\n final K key;\n V value;\n Node next;\n Node(int hash, K key, V value, Node next) {\n this.hash = hash;\n this.key = key;\n this.value = value;\n this.next = next;\n }\n\n public final K getKey() { return key; }\n public final V getValue() { return value; }\n public final String toString() { return key + \"=\" + value; }\n\n public final int hashCode() {\n return Objects.hashCode(key) ^ Objects.hashCode(value);\n }\n\n public final V setValue(V newValue) {\n V oldValue = value;\n value = newValue;\n return oldValue;\n }\n\n public final boolean equals(Object o) {\n if (o == this)\n return true;\n if (o instanceof Map.Entry) {\n Map.Entry,?> e = (Map.Entry,?>)o;\n if (Objects.equals(key, e.getKey()) &&\n Objects.equals(value, e.getValue()))\n return true;\n }\n return false;\n }\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"向上继承 "},{"type":"text","marks":[{"type":"strong"}],"text":"Map.Entry"},{"type":"text","text":" 接口,并实现了 "},{"type":"text","marks":[{"type":"strong"}],"text":"getKey"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"strong"}],"text":"getValue"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"strong"}],"text":"toString"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"strong"}],"text":"hashCode"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"strong"}],"text":"setValue"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"strong"}],"text":"equals"},{"type":"text","text":" 这六个方法。之后自己在实现一个链表 "},{"type":"text","marks":[{"type":"strong"}],"text":"Node"},{"type":"text","text":" 的内部类,这个链表的节点都保存三个东西,hash key value 这三个变量。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来,我们从创建一个 "},{"type":"text","marks":[{"type":"strong"}],"text":"HashMap"},{"type":"text","text":" 入手。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"创建 HashMap"},{"type":"link","attrs":{"href":"https://blog.sincehub.cn/2020/10/31/jdk-HashMap/#%E5%88%9B%E5%BB%BA-hashmap","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常来讲,"},{"type":"text","marks":[{"type":"strong"}],"text":"HashMap"},{"type":"text","text":" 有三种 new 的方式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\nMap first = new HashMap<>();\n\nMap second = new HashMap<>(12);\n\nMap third = new HashMap<>(first);\n\nMap fourth = new HashMap<>(12 , 1);\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一种是无参构造,第二种是传入一个 int 值,第三种是允许传入一个 "},{"type":"text","marks":[{"type":"strong"}],"text":"Map"},{"type":"text","text":" 实现的任何子类,第四种是两个 int 类型。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\n/**\n * Constructs an empty HashMap with the default initial capacity\n * (16) and the default load factor (0.75).\n */\n// first 的构造\npublic HashMap() {\n this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted\n}\n\n/**\n * Constructs an empty HashMap with the specified initial\n * capacity and the default load factor (0.75).\n *\n * @param initialCapacity the initial capacity.\n * @throws IllegalArgumentException if the initial capacity is negative.\n */\n// second 的构造\npublic HashMap(int initialCapacity) {\n this(initialCapacity, DEFAULT_LOAD_FACTOR);\n}\n\n/**\n * Constructs a new HashMap with the same mappings as the\n * specified Map. The HashMap is created with\n * default load factor (0.75) and an initial capacity sufficient to\n * hold the mappings in the specified Map.\n *\n * @param m the map whose mappings are to be placed in this map\n * @throws NullPointerException if the specified map is null\n */\n// third 的构造\npublic HashMap(Map extends K, ? extends V> m) {\n this.loadFactor = DEFAULT_LOAD_FACTOR;\n putMapEntries(m, false);\n}\n\n/**\n * Constructs an empty HashMap with the specified initial\n * capacity and load factor.\n *\n * @param initialCapacity the initial capacity\n * @param loadFactor the load factor\n * @throws IllegalArgumentException if the initial capacity is negative\n * or the load factor is nonpositive\n */\n// fourth 的构造\npublic HashMap(int initialCapacity, float loadFactor) {\n if (initialCapacity < 0)\n throw new IllegalArgumentException(\"Illegal initial capacity: \" +\n initialCapacity);\n if (initialCapacity > MAXIMUM_CAPACITY)\n initialCapacity = MAXIMUM_CAPACITY;\n if (loadFactor <= 0 || Float.isNaN(loadFactor))\n throw new IllegalArgumentException(\"Illegal load factor: \" +\n loadFactor);\n this.loadFactor = loadFactor;\n this.threshold = tableSizeFor(initialCapacity);\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里提到三个变量,分别 "},{"type":"text","marks":[{"type":"strong"}],"text":"initialCapacity"},{"type":"text","text":"、"},{"type":"text","marks":[{"type":"strong"}],"text":"loadFactor"},{"type":"text","text":"、"},{"type":"text","marks":[{"type":"strong"}],"text":"threshold"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看中文意思,分别是初始化容量、负载因子、阈值。其中负载因子,也就是 "},{"type":"text","marks":[{"type":"strong"}],"text":"loadFactor"},{"type":"text","text":" 默认是 0.75f"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\n/**\n * The load factor used when none specified in constructor.\n */\nstatic final float DEFAULT_LOAD_FACTOR = 0.75f;\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 "},{"type":"text","marks":[{"type":"strong"}],"text":"third"},{"type":"text","text":" 这个构造函数里面,有个 "},{"type":"text","marks":[{"type":"strong"}],"text":"putMapEntries"},{"type":"text","text":" 的方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\n /**\n * Implements Map.putAll and Map constructor.\n * 构造方法的时候就全部添加进去 hashMap\n * @param m the map 传入的Map\n * @param evict false when initially constructing this map, else\n * true (relayed to method afterNodeInsertion). 构造使用到就是 false 非构造就是 true\n */\n final void putMapEntries(Map extends K, ? extends V> m, boolean evict) {\n // Map 的大小\n int s = m.size();\n if (s > 0) {\n // 由于是构造调用的,table = null\n if (table == null) { // pre-size\n // 在调用次方法的时候,先赋值了 loadFactor = 0.75。所以ft = (Map 长度 / 0.75) + 1.0\n float ft = ((float)s / loadFactor) + 1.0F;\n // 判断是否超出 MAXIMUM_CAPACITY = 1 << 30 也就是 2 的30次幂大小\n int t = ((ft < (float)MAXIMUM_CAPACITY) ?\n (int)ft : MAXIMUM_CAPACITY);\n // 起始 threshold = 0,这个也在 fourth 的构造里面使用到了,下面有解释\n if (t > threshold)\n // 如果 t 的大小在 2 的(n-1)次幂 与 2 的 n 次幂之间,则 t 的大小为 2 的n次幂。不懂没关系,下面有解释、举例\n threshold = tableSizeFor(t);\n }\n // Map的长度 是否 大于 刚刚 tableSizeFor 之后的长度\n // 首次是不会大于这个值的\n else if (s > threshold)\n // 扩容,后面对这个方法讲解\n resize();\n \n // 将 Map 的子类对象 放进HashMap\n for (Map.Entry extends K, ? extends V> e : m.entrySet()) {\n K key = e.getKey();\n V value = e.getValue();\n // 先通过位运算减少 key 的冲突,在放进去\n putVal(hash(key), key, value, false, evict);\n }\n }\n }\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"putMapEntries"},{"type":"text","text":" 这个方法就是 HashMap 在构造的时候,放进 "},{"type":"text","marks":[{"type":"strong"}],"text":"Map"},{"type":"text","text":" 的对象,那么就会将他是所有 k v 都放进去,然后初始化长度,这个长度是当前 Map 长度靠近 2 n次幂 长度。比如"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Map 7,则HashMap 初始化8,"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果是12 则是 16,"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果是16 则是 16."}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"之后判断是否需要扩容,由于是首次,所有不需要,接着就是放进 HashMap。先通过位运算计算 Hash 减少冲突,在pull。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\n /**\n * Computes key.hashCode() and spreads (XORs) higher bits of hash\n * to lower. Because the table uses power-of-two masking, sets of\n * hashes that vary only in bits above the current mask will\n * always collide. (Among known examples are sets of Float keys\n * holding consecutive whole numbers in small tables.) So we\n * apply a transform that spreads the impact of higher bits\n * downward. There is a tradeoff between speed, utility, and\n * quality of bit-spreading. Because many common sets of hashes\n * are already reasonably distributed (so don't benefit from\n * spreading), and because we use trees to handle large sets of\n * collisions in bins, we just XOR some shifted bits in the\n * cheapest possible way to reduce systematic lossage, as well as\n * to incorporate impact of the highest bits that would otherwise\n * never be used in index calculations because of table bounds.\n *\n * 通过位运算减少 Hash 冲突\n */\n static final int hash(Object key) {\n int h;\n return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);\n }\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 "},{"type":"text","marks":[{"type":"strong"}],"text":"fourth"},{"type":"text","text":" 这个构造函数里面,还有一个这 "},{"type":"text","marks":[{"type":"strong"}],"text":"tableSizeFor"},{"type":"text","text":" 的方法,上面有用到。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\nstatic final int tableSizeFor(int cap) {\n int n = cap - 1;\n n |= n >>> 1;\n n |= n >>> 2;\n n |= n >>> 4;\n n |= n >>> 8;\n n |= n >>> 16;\n return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"进行了位运算,由于前面好保证了 cap 非负数。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"»>"},{"type":"text","text":" 右移,高位补零。比如 0001,使用 »> 1 之后就是 00001,对其长度,则1去掉。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"**** 或操作,不同取1,相同1取1,相同0取0"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"tableSizeFor"},{"type":"text","text":" 方法里面的意思就是,如果 cap 介于 "},{"type":"text","marks":[{"type":"strong"}],"text":"2 (n - 1)次幂"},{"type":"text","text":" 与"},{"type":"text","marks":[{"type":"strong"}],"text":"2 n次幂"},{"type":"text","text":"之间,cap 的值为 "},{"type":"text","marks":[{"type":"strong"}],"text":"2 n次幂 - 1"},{"type":"text","text":" 大小。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\nint n = 18;\nn |= n >>> 1;\nn |= n >>> 2;\nn |= n >>> 4;\nn |= n >>> 8;\nn |= n >>> 16;\nSystem.err.println(n); // 31\n\nint n = 16;\nn |= n >>> 1;\nn |= n >>> 2;\nn |= n >>> 4;\nn |= n >>> 8;\nn |= n >>> 16;\nSystem.err.println(n); // 31\n\nint n = 14;\nn |= n >>> 1;\nn |= n >>> 2;\nn |= n >>> 4;\nn |= n >>> 8;\nn |= n >>> 16;\nSystem.err.println(n); // 15\n\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最后只要算的 n 非负数,且不大于 int 类型最大值,则 n + 1。也就是说这个初始容量,无论设置什么数字,都会是 2 的倍数。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"HashMap 添加"},{"type":"link","attrs":{"href":"https://blog.sincehub.cn/2020/10/31/jdk-HashMap/#hashmap-%E6%B7%BB%E5%8A%A0","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们常常使用 kv 的形式存储。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\n map.put(\"sin\", \"sy\");\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"点进去看看,"},{"type":"text","marks":[{"type":"strong"}],"text":"HashMap"},{"type":"text","text":" 如何存储。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\n // 首先来到父类定义的\n V put(K key, V value);\n\n\n /**\n * Associates the specified value with the specified key in this map.\n * If the map previously contained a mapping for the key, the old\n * value is replaced.\n *\n * @param key key with which the specified value is to be associated\n * @param value value to be associated with the specified key\n * @return the previous value associated with key, or\n * null if there was no mapping for key.\n * (A null return can also indicate that the map\n * previously associated null with key.)\n * hashMap 的添加\n */\n // 然后实现父类定义的方法\n public V put(K key, V value) {\n // hash 方法上面有提到作用\n return putVal(hash(key), key, value, false, true);\n }\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"pull value 的时候,传入先经过位运算的 key 的 hashCode,然后传 key value、false,true"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\n /**\n * Implements Map.put and related methods.\n *\n * @param hash hash for key\n * @param key the key\n * @param value the value to put\n * @param onlyIfAbsent if true, don't change existing value 这个值在pull 的时候是false\n * @param evict if false, the table is in creation mode. 这个值在pull 的时候是 true\n * @return previous value, or null if none\n */\n final V putVal(int hash, K key, V value, boolean onlyIfAbsent,\n boolean evict) {\n // 初始化两个容器 一个 k v 数组链表、一个是链表\n Node[] tab; Node p; int n, i;\n // 第一次这里是null,所以 n = (tab = resize()).length;\n if ((tab = table) == null || (n = tab.length) == 0)\n // resize 下面有讲解\n // 是否扩容处理,没有扩容,则是当前长度,有则是之前的两倍\n n = (tab = resize()).length;\n // 因为数组从0开始计算,所以是 n - 1 取模,确定值放置在数值链表位置\n // 如果当前数组链表为null,同时 p = 链表table\n if ((p = tab[i = (n - 1) & hash]) == null)\n // 创建一个链表\n // 没有key值,就给你创建一个\n tab[i] = newNode(hash, key, value, null);\n // 有值的情况下\n else {\n // 数组链表头有数据\n // 初始化 链表 泛型k\n Node e; K k;\n // hash 值一致且key值的地址也一样 或者 key不为null 且key的值相同\n // 大概就是相同key,替换value\n if (p.hash == hash &&\n ((k = p.key) == key || (key != null && key.equals(k))))\n e = p;\n // 如果 p 是 树结构\n else if (p instanceof TreeNode)\n // 按树的形式 pul 进去\n e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);\n // 值不相同,也不是树结构,且链表头还相同,同一节点底下还有链表\n else {\n // 向下遍历\n for (int binCount = 0; ; ++binCount) {\n // 链表下一节点为null\n if ((e = p.next) == null) {\n // p的下一节点指向创建一个新的链表\n p.next = newNode(hash, key, value, null);\n // 当链表的长度大于等于7,因为从1开始,也是说链表头有值,所以才从一开始。另外这是 ++1 性能比 i++好,i++比起++i多了一个创建临时变量(放入数据栈)的步骤,因此效率低一些\n if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st\n // 链表转树结构\n treeifyBin(tab, hash);\n break;\n }\n // key 值相同\n if (e.hash == hash &&\n ((k = e.key) == key || (key != null && key.equals(k))))\n break;\n p = e;\n }\n }\n // 当链表不为null,建立映射,kv\n if (e != null) { // existing mapping for key\n V oldValue = e.value;\n // onlyIfAbsent 如果是 true 不改变现有的值,false 改变,或者 oldValue == null\n if (!onlyIfAbsent || oldValue == null)\n // 完成关联\n e.value = value;\n // 尾插,链表直接插到当前链表的最后一个位置上\n afterNodeAccess(e);\n return oldValue;\n }\n }\n // 快速失败机制,比较当前操作次数与记录次数,是否不一样\n ++modCount;\n // 因为完成一个 pulValue 所以增加一次次数,同时再次比较是否来到阈值\n if (++size > threshold)\n // 来到阈值,2倍扩容容器\n resize();\n // true 如果是头结点则删除\n afterNodeInsertion(evict);\n return null;\n }\n\n\n /**\n * Initializes or doubles table size. If null, allocates in\n * accord with initial capacity target held in field threshold.\n * Otherwise, because we are using power-of-two expansion, the\n * elements from each bin must either stay at same index, or move\n * with a power of two offset in the new table.\n * 具有初始化容器大小、2倍扩容容器等功能\n * @return the table\n */\n final Node[] resize() {\n // 首次 table 是null\n Node[] oldTab = table;\n // 判断 oldCap 是否为 null 为null 则为0,反之则是容器长度\n int oldCap = (oldTab == null) ? 0 : oldTab.length;\n // 阈值 首次是0\n int oldThr = threshold;\n // 新容器容量0 新容器阈值0\n int newCap, newThr = 0;\n // 容量大于0\n if (oldCap > 0) {\n // 大于等于最大能容纳的长度\n if (oldCap >= MAXIMUM_CAPACITY) {\n // 阈值等于最大能容纳的长度\n threshold = Integer.MAX_VALUE;\n return oldTab;\n }\n // 容器长度是否大于默认16,且新容器是旧容器的两倍大小且不大于最大容纳长度,\n else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&\n oldCap >= DEFAULT_INITIAL_CAPACITY)\n // 新阈值也是旧阈值的两倍大小\n newThr = oldThr << 1; // double threshold\n }\n // 旧阈值 大于 0\n else if (oldThr > 0) // initial capacity was placed in threshold\n // 新容器长度 = 旧阈值\n newCap = oldThr;\n else { // zero initial threshold signifies using defaults\n // 首次 pull的时候\n // 新容器长度 = 16\n newCap = DEFAULT_INITIAL_CAPACITY;\n // 新阈值等于 负载因子 0.75 * 默认长度 16 = 12\n newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);\n }\n // 这个判断只有在第一次使用构造方法传入一个 Map 才会用到,且这个Map 有值,其他是都基本走不到这里\n if (newThr == 0) {\n // 容器长度 * 负载因子 0.75\n float ft = (float)newCap * loadFactor;\n // 只要不大于最大容纳的长度,则 newThr = ft\n newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?\n (int)ft : Integer.MAX_VALUE);\n }\n // 赋值到类变量里面,基本确定阈值\n threshold = newThr;\n @SuppressWarnings({\"rawtypes\",\"unchecked\"})\n // 新数组链表 等于 新容器的长度\n Node[] newTab = (Node[])new Node[newCap];\n // 赋值到类的变量里面\n table = newTab;\n // 旧容器不为null\n // 这是是首次 oldTab 为 null 不走这里,比如第一次 pull 构造方法放入map的子类\n // 之后 pull 这里都会走一遍\n if (oldTab != null) {\n // 链表循环指向下一个节点\n for (int j = 0; j < oldCap; ++j) {\n Node e;\n // 先赋值,当前数组链表的链表给 e 链表且不为null\n if ((e = oldTab[j]) != null) {\n // 设置数组链表为null\n oldTab[j] = null;\n // 链表的下一节点为null\n if (e.next == null)\n // 只有数组链表头有数据,子节点没有数据\n // hash 取模,这个模的长度是数组的长度,e 接到新的容器底下\n newTab[e.hash & (newCap - 1)] = e;\n // 判断 e 是否是树结构\n else if (e instanceof TreeNode)\n ((TreeNode)e).split(this, newTab, j, oldCap);\n // e 不是树结构,且数组链表的子节点也有数据\n // 这里应该是计算不同hash的节点,到不同链表上\n // 然后迁移到新的容器里,对应的数组链表的位置\n else { // preserve order\n // \n Node loHead = null, loTail = null;\n Node hiHead = null, hiTail = null;\n // 下一节点\n Node next;\n do {\n // e 指向下一节点\n next = e.next;\n // e 的hash 取模 oldCap,分散。\n if ((e.hash & oldCap) == 0) {\n // 第一次为null\n if (loTail == null)\n // loHead 是第一值\n loHead = e;\n else\n // loTail的下一节点 指向 e的下一节点\n loTail.next = e;\n // 不是最后一个位置\n loTail = e;\n }\n // \n else {\n // 和上面的步骤差不多\n if (hiTail == null)\n hiHead = e;\n else\n hiTail.next = e;\n hiTail = e;\n }\n } while ((e = next) != null);\n // 这里是原来数组链表的长度\n if (loTail != null) {\n loTail.next = null;\n newTab[j] = loHead;\n }\n // 2倍扩容之后的数组长度,换句话说,之前的数据,如果 hash 取模之后不等于,之前最后面的位置,则一律变成之前的位置 加上 旧数组长度的。\n // 如果之前是 2 数组链表长度是 8,之后这个位置来到 10\n if (hiTail != null) {\n hiTail.next = null;\n newTab[j + oldCap] = hiHead;\n }\n }\n }\n }\n }\n return newTab;\n }\n\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"总结下,在 pulValue 的时候做了什么。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"先初始化数据结构,数组链表"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"如果没有设置初始化大小,则默认长度为16,同时设置阈值为 0.75 * 当前长度"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"如果长度来到阈值大小,则进行当前容量二倍扩容,数据大部分会移到新的长度链表里面,少部分会遗留在旧容器长度里面"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"查看插入 key 是否存在,有则尾插入,插入之后是否来到 8 ,链表长度来到8则开始转成红黑树。没有,直接插入"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"然后 key 的位置里,在放入 value。完成关联"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","text":"记录操作次数"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":7,"align":null,"origin":null},"content":[{"type":"text","text":"插入后,在继续判断是否扩容"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面,少了链表如何转红黑树。接下来,我们继续看看"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\n /**\n * Replaces all linked nodes in bin at index for given hash unless\n * table is too small, in which case resizes instead.\n */\n final void treeifyBin(Node[] tab, int hash) {\n int n, index; Node e;\n // 链表长度是否小于 64,换句话说,树长最长只有64\n if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)\n // 当链表长度,来到 8 的时候也会判断是否扩容\n resize();\n // 确定位置\n else if ((e = tab[index = (n - 1) & hash]) != null) {\n TreeNode hd = null, tl = null;\n do {\n // 链表一个一个替换成树节点\n TreeNode p = replacementTreeNode(e, null);\n if (tl == null)\n hd = p;\n else {\n p.prev = tl;\n tl.next = p;\n }\n tl = p;\n } while ((e = e.next) != null);\n if ((tab[index] = hd) != null)\n // 树节点,在转成红黑树,可以认为树节点拼接成一个树\n hd.treeify(tab);\n }\n }\n\n\n /**\n * Forms tree of the nodes linked from this node.\n */\n final void treeify(Node[] tab) {\n TreeNode root = null;\n for (TreeNode x = this, next; x != null; x = next) {\n next = (TreeNode)x.next;\n x.left = x.right = null;\n // 起始 red = false,当 = false 时候就是 黑色,同时根节放入值,只有首次才会走到\n if (root == null) {\n x.parent = null;\n x.red = false;\n root = x;\n }\n else {\n K k = x.key;\n int h = x.hash;\n Class> kc = null;\n // 遍历链表\n for (TreeNode p = root;;) {\n int dir, ph;\n K pk = p.key;\n if ((ph = p.hash) > h)\n dir = -1;\n else if (ph < h)\n dir = 1;\n else if ((kc == null &&\n (kc = comparableClassFor(k)) == null) ||\n (dir = compareComparables(kc, k, pk)) == 0)\n dir = tieBreakOrder(k, pk);\n\n TreeNode xp = p;\n if ((p = (dir <= 0) ? p.left : p.right) == null) {\n x.parent = xp;\n if (dir <= 0)\n xp.left = x;\n else\n xp.right = x;\n // 这里通过左旋 或者右旋,使得 黑的节点变成父节点,自旋完成后,父节点为黑色,而子节点都是红色\n root = balanceInsertion(root, x);\n break;\n }\n }\n }\n }\n moveRootToFront(tab, root);\n }\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"转红黑树的过程中,首先会进行一次判断扩容处理,之后先变成一个个的树节点,在变成红黑树。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中 red = false 时,为黑,反之则是红。通过左右旋,使其父节点变成黑色,子节点为红色,完成一次平衡处理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"图如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ad/ad859eac23be068ea77af6cd91d52a1c.gif","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首次,root节点就是只有一个,所以直接是黑。当当前树高相差为3,会完成一次自旋,使其平衡,如果是不平衡的节点,则会变成红色,平衡的节点则为黑色。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"HashMap 的数据结构图解"},{"type":"link","attrs":{"href":"https://blog.sincehub.cn/2020/10/31/jdk-HashMap/#hashmap-%E7%9A%84%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%9B%BE%E8%A7%A3","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从上面,我们基本了解了,hashMap是怎么扩容的,如何链表转红黑树、如何定位位置。接下来我们看看HashMap的数据结构是如何。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"起始的结构是由数组链表里面且是一个kv结构。头部是一个数组,k的位置是一个 hash 取模(位扰动之后的hash)之后,确定的位置。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/73/73a372ed0c21e103d4eb1f813c750ba5.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"后来当,hash冲突变多的时候,数组底下的链表就是存储 hash 冲突之后存储的位置,这个长度大于8的时候转红黑树,包括头,也就是数组,底下的链表长度是7。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d8/d8e8db22ab2b3435dec152a6d03134d3.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"优化以及使用 HashMap 的建议"},{"type":"link","attrs":{"href":"https://blog.sincehub.cn/2020/10/31/jdk-HashMap/#%E4%BC%98%E5%8C%96%E4%BB%A5%E5%8F%8A%E4%BD%BF%E7%94%A8-hashmap-%E7%9A%84%E5%BB%BA%E8%AE%AE","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"既然读源码,就是为了更好的了解这个东西,只为读而读,为面试而读,这个源码读的没什么意思。学完得要有自己体会吧~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"当使用的 "},{"type":"text","marks":[{"type":"strong"}],"text":"HashMap"},{"type":"text","text":" 是固定不变或者未达到 2 的n次方长度,比如只使用到3,而当前 HashMap 长度为4,可以直接设置负载因子,固定其长度,避免过早的扩容,而浪费空间。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"HashMap"},{"type":"text","text":" 默认初始化大小为16,如果添加的值,不到16的话,甚至只有它的一半,这样很容易造成空间上的浪费,之后过多的空间浪费,从而触发 GC 回收,降低整个系统的响应时间。"},{"type":"text","marks":[{"type":"strong"}],"text":"所以尽可能的初始化大小,避免空间的浪费"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"当使用的 "},{"type":"text","marks":[{"type":"strong"}],"text":"HashMap"},{"type":"text","text":" 长度超过16,都是没有初始化容器大小,来到阈值大小,频繁的触发扩容机制,导致 "},{"type":"text","marks":[{"type":"strong"}],"text":"HashMap"},{"type":"text","text":" 性能下降。"},{"type":"text","marks":[{"type":"strong"}],"text":"所以也尽可能的初始化大小,避免频繁触发扩容机制。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"HashMap"},{"type":"text","text":" 线程不安全,这里主要是这两点(JDK8)通过源码我们知道,在添加元素的时候,在添加前以及添加后都会判断是否扩容,而改变 kv 的分布。在多线程中,如果 A 线程先添加完值,改变了整个容器值的分布,而 B 线程拿到的是原先容器大小来判断位置,从而导致插入的值不在正确的位置上。"},{"type":"text","marks":[{"type":"strong"}],"text":"HashMap"},{"type":"text","text":" 里面有记录操作次数,在多线程中,当前的操作次数,与记录操作次数,可能会不正确,(可能拿到旧值)而出发快速失败机制。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"只要是继承了 "},{"type":"text","marks":[{"type":"strong"}],"text":"Map"},{"type":"text","text":" 接口的子类,都可以转成 HashMap,在创建对象的时候。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"声明"},{"type":"link","attrs":{"href":"https://blog.sincehub.cn/2020/10/31/jdk-HashMap/#%E5%A3%B0%E6%98%8E","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作者: Sinsy"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文链接:https://blog.sincehub.cn/2020/10/31/jdk-hashmap/"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"版权声明:本文为博主原创文章,遵循 "},{"type":"link","attrs":{"href":"https://creativecommons.org/licenses/by-sa/4.0/deed.zh","title":null},"content":[{"type":"text","text":"CC 4.0 BY-SA "}]},{"type":"text","text":"版权协议,转载请附上原文声明。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如您有任何商业合作或者授权方面的协商,请给我留言:[email protected]"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"引用"},{"type":"link","attrs":{"href":"https://blog.sincehub.cn/2020/10/31/jdk-HashMap/#%E5%BC%95%E7%94%A8","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[1] "},{"type":"link","attrs":{"href":"https://www.cs.usfca.edu/~galles/visualization/Algorithms.html","title":null},"content":[{"type":"text","text":"Data Structure Visualizations"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章