java数据结构之HashMap
一、HashMap源码注释
/** * HashMap的存储结构: * 底层是用一个数组table来存储键值对的Node,table的每个下标称为一个桶,通过key计算出来的hash值和table的长度计算出来下标值,键值对就存储在table的该下标处的桶中 * 如果计算得到的下标相同就会被存入到同一个桶里面,以单链表的形式来进行存储, * 如果链表太长就会以tree的形式来对Node进行存储 * * * HashMap实现了Map接口,允许存储null键和null值。 * HashMap和HashTable相比除了它是不同步的并且允许null键和null值外,其他部分大致相当。 * HashMap不能保证map中元素的顺序,也不能保证顺序不随着时间的变化改变 * * 如果hash函数将元素恰当的分散到各个桶中,那么HashMap为基本操作提供了get和put提供了常量级的时间性能。 * 迭代集合所需要的时间和桶的个数以及键值对的个数成正比。因此,如果很看重性能就不要将初始容量设置太高(或者负载因子设置太小) * * HashMap实例有两个影响其性能的参数:初始容量initial capacity和负载因子load factor。 * initial capacity 就是创建HashMap时table的长度。 * load factor 负载因子 是衡量在table的长度在被自动增加之前,table可以填多满的度量,用来限制table填充的极限。 * 当哈希表的中的键值对超过负载因子和当前容量的乘积时,哈希表将会被rehash(即重新构建内部数据结构),导致table的桶数量翻倍 * * 一般来说默认的负载因子0.75在时间和空间成本之间提供了很好的权衡,较高的负载因子虽然减少了空间开销,但是增加了查询的成本(反映在HashMap的大多数操作上,包括put和get)。 * 在设置map的初始容量时,应该考虑map中预期的键值对数量以及负载因子,从而最小化rehash操作数量。 * 如果初始容量大于最大键值对数除以负载因子,则不会发生任何重哈希操作。 * * 如果要将许多映射存储在HashMap实例中,那么创建足够大容量的哈希表来存储映射将比让映射根据需要执行自动哈希来增长表更有效地存储映射。 * 注意,使用具有相同hashCode()的多个键肯定会降低任何散列表的性能。为了改善影响,当键是可比较的时,该类可以使用键之间的比较顺序来帮助降低影响。 * * 注意,HashMap不是同步的。如果多个线程同时访问一个散列映射,并且其中至少有一个线程从结构上修改了映射,那么它必须在外部同步。 * 这通常是通过在封装这个map的对象上进行同步来实现的。如果不存在这样的对象,则应该使用Collections.synchronizedMap“包装”映射 * 方法。这最好在创建时完成,以防止意外地不同步地访问这个map * Map m = Collections.synchronizedMap(new HashMap(...)); * * 这个类的所有“集合视图方法”返回的迭代器都是快速失败的:如果在创建迭代器之后的任何时候对映射进行结构上的修改, * (除了通过迭代器自己的remove方法之外),迭代器将抛出ConcurrentModificationException。 * 因此,在面对并发修改时,迭代器会快速而干净地失败,而不是在将来某个不确定的时间冒着任意的、不确定的行为的风险。 * * 注意,迭代器的快速故障行为不能得到保证,一般来说,在存在非同步并发修改的情况下,不可能做出任何严格的保证。 * 故障快速迭代器以最大的努力抛出ConcurrentModificationException。 * 因此,编写一个依赖于此异常来判断其正确性的程序是错误的:迭代器的快速故障行为应该只用于检测bug。 * * * @author Doug Lea * @author Josh Bloch * @author Arthur van Hoff * @author Neal Gafter */ public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable { private static final long serialVersionUID = 362498820763181265L; /* * 注意事项: * * HashMap通常相当于装有很多桶哈希表,但是当桶变得太大时,它们会被转换成树节点的桶,每个桶的结构都类似于java.util.TreeMap中的桶。 * 大多数方法都尝试使用普通的桶(链表),但在适用时中继到TreeNode方法(只需检查节点的instanceof)。 * 树节点的存储桶可以像其他存桶一样被遍历,但是可以在键值对数量很多的时支持更快的查找。 * 但是,由于正常使用的大多数桶并没有过度填充,所以在表方法的过程中可能会延迟检查树桶是否存在。 * * 树型桶(其元素都是treenode的桶)主要由hashCode排序,但在tie的情况下, * 如果两个元素具有相同的“class C implementation Comparable”,则键入它们的compareTo方法来排序。 * 树桶的额外复杂性是值得的,在提供坏的O (log n)操作,当键有不同的散列或可排序时。 * 因此,在意外或恶意使用hashCode()方法的返回值有着很差的分布时,以及那些许多键共享一个hashCode的键(只要它们也是可比较的),性能会优雅地下降, * (如果这两种方法都不适用,与不采取预防措施相比,我们可能会浪费大约两倍的时间和空间。 * 但目前所知的唯一案例来自于糟糕的用户编程实践,这些实践已经非常缓慢,以至于没有什么区别。) * * 因为树节点的大小大约是普通节点的两倍,所以我们只在桶中包含足够的节点以保证使用时才使用它们(请参阅TREEIFY_THRESHOLD)。 * 当它们变得太小(由于移除或调整大小),就会被转换回普通的桶(链表)。在使用分布良好的用户哈希码时,很少使用树桶。 * 理想情况下,在随机哈希码下,bin中节点的频率遵循泊松分布(http://en.wikipedia.org/wiki/Poisson_distribution), * 默认调整阈值为0.75,平均参数约为0.5,尽管由于调整粒度而存在较大的差异。忽略方差,列表大小k的预期出现率为(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 * more: less than 1 in ten million * 更多:少于千万分之一 * * 树桶的根节点通常是它的第一个节点,但是有时(目前仅在Iterator.remove之后)根可能在其他地方, * 但是能够通过TreeNode.root()方法进行恢复 * * 所有适用的内部方法都接受散列代码作为参数(通常由公共方法提供),允许它们在不重新计算用户散列代码的情况下相互调用。大多数内部方法也接受“tab”参数, * 这通常是当前表,但在调整大小或转换时可能是新的或旧的。 * * 当桶列表被treeified、split或untreeified时,我们将它们保持相同的相对访问/遍历顺序 * (即为了更好地保存局部,并稍微简化对调用iterator.remove的分割和遍历的处理。 * 当在插入时使用比较器时,为了保持跨重新平衡的总顺序(或尽可能接近这里的要求),我们将类和identityhashcode作为连接符进行比较。 * * 由于LinkedHashMap子类的存在,普通vs树模式之间的使用和转换变得复杂。有关定义在插入、删除和访问时调用的回调方法, * 请参见下面,这些方法允许LinkedHashMap内部保持独立于这些机制。(这还要求将map实例传递给一些可能创建新节点的实用程序方法。) * */ /** * 默认的初始化table的大小16,必须是2的整数次幂 */ static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 /** * 最大容量大小,必须是小于1<<30的2的整数次幂 */ static final int MAXIMUM_CAPACITY = 1 << 30; /** * 默认的加载因子 */ static final float DEFAULT_LOAD_FACTOR = 0.75f; /** * 要想桶转化为树形桶,则该桶中至少要有TREEIFY_THRESHOLD个元素。这个值必须要大于2,大于等于8最好 */ static final int TREEIFY_THRESHOLD = 8; /** * 当树桶中的元素数量小于等于UNTREEIFY_THRESHOLD时,就需要将树桶转化为链表 */ static final int UNTREEIFY_THRESHOLD = 6; /** * 想要桶转为树桶所需要的最小桶数量,这个值应至少为4 * TREEIFY_THRESHOLD,以避免调整大小和treeification阈值之间的冲突。 * 如果桶数量小于这个值,就算桶中的元素已经达到了TREEIFY_THRESHOLD值,也只是通过resize来调整表中桶的个数 */ static final int MIN_TREEIFY_CAPACITY = 64; /** * 基本的桶节点,用于大多数entries(参见下面的TreeNode子类和LinkedHashMap中的Entry子类) */ static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; 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; } } /* ---------------- Static utilities -------------- */ /** * 计算key的hash值 * 当key为null时,hash值为0,否则hash值为key的hashcode高16位和低16位进行异或运算 * * 为什么要这样设计? * 因为通过hashtable计算下标的方式i = (n - 1) & hash ,n为table的桶的个数可知hash值的后几位参与了运算。 * 设计者权衡了speed, utility, and quality,将高16位与低16位异或来减少这种影响。 * 考虑到现在的hashCode分布的已经很不错了,而且当发生较大碰撞时也用树形存储降低了冲突。 * 仅仅异或一下,既减少了系统的开销,也不会造成的因为高位没有参与下标的计算(table长度比较小时),从而引起的碰撞。 */ static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } /** * Returns x's Class if it is of the form "class C implements * Comparable<C>", else null. */ static Class<?> comparableClassFor(Object x) { if (x instanceof Comparable) { Class<?> c; Type[] ts, as; Type t; ParameterizedType p; if ((c = x.getClass()) == String.class) // bypass checks 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) // type arg is c return c; } } } return null; } /** * 如果x不等于null且x的类对象为kc则返回k.compareTo(x)的值,否则返回0。其中k是筛选过的可比较的类 */ @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable static int compareComparables(Class<?> kc, Object k, Object x) { return (x == null || x.getClass() != kc ? 0 : ((Comparable)k).compareTo(x)); } /** * 返回最近的不小于cap的2的整数次幂 * 为什么要cap-1?是为了防止cap本身就是2的幂,最后得到的结果是cap的两倍,这样就不是最近的不小于cap的2的整数次幂 * 下面的5个唯一操作是为了将cap的最高位的1后面的所有位都置为1,这样得到的结果肯定是2的幂次方减1。 * 最后如果n已经大于最大的容量MAXIMUM_CAPACITY,那么就取MAXIMUM_CAPACITY,否则将n+1,这样就得到了离cap最近的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; } /* ---------------- Fields -------------- */ /** * HashMap底层的数组 */ transient Node<K,V>[] table; /** * Holds cached entrySet(). Note that AbstractMap fields are used * for keySet() and values(). */ transient Set<Map.Entry<K,V>> entrySet; /** * HashMap中键值对的个数 */ transient int size; /** * HashMap结构化修改的次数 */ transient int modCount; /** * HashMap需要扩容时,HashMap中键值对需要达到的容量,其值等于 当前容量*加载因子 */ int threshold; /** * 加载因子 */ final float loadFactor; /* ---------------- Public operations -------------- */ /** * 指定初始化容量和加载因子来构建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);//直接先将threshold的大小table的size,后面resize方法就用到了 } /** * 指定容量创建HashMap,使用默认加载因子0.75 */ public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } /** * 使用默认容量16和默认加载因子0.75来创建HashMap对象 */ public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted } /** * 利用其他map来创建HashMap对象 */ public HashMap(Map<? extends K, ? extends V> m) { this.loadFactor = DEFAULT_LOAD_FACTOR; putMapEntries(m, false); } /** * 在调用putAll或者Map的构造方法时会用到 */ final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) { int s = m.size(); if (s > 0) { if (table == null) { // pre-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(); for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) { K key = e.getKey(); V value = e.getValue(); putVal(hash(key), key, value, false, evict); } } } /** * 返回HashMap中键值对的个数 */ public int size() { return size; } /** * 判断是否为空 */ public boolean isEmpty() { return size == 0; } /** * 通过key获取value */ public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; } /** * 通过key和key的 hash值来获取Node,供get、containsKey等方法调用 * 可以看出是通过(n - 1) & hash来确定桶的位置 */ 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 && //判断桶的第一个节点就是要找的节点 ((k = first.key) == key || (key != null && key.equals(k)))) return first; if ((e = first.next) != null) {//如果第一个节点后面还有节点就往下找 if (first instanceof TreeNode)//如果第一个节点是树节点就用树的方式去查找 return ((TreeNode<K,V>)first).getTreeNode(hash, key); do {//如果不是树节点,那么就是链表节点,就直接循环遍历 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; } /** * 判断是否存在该key */ public boolean containsKey(Object key) { return getNode(hash(key), key) != null; } /** * 新增键值对,不管key是否已经存在 */ public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } /** * 新增键值对 * hash 是key调用hash方法得到的值 * key 是键 * value 是值 * onlyIfAbsent 是否只有在key不存在的情况下覆盖原来的value false表示覆盖 */ final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0)//如果table为null或者空,调用resize方法进行初始化 n = (tab = resize()).length; //创建一个Node数组,长度为DEFAULT_INITIAL_CAPACITY,threshold为DEFAULT_INITIAL_CAPACITY * loadFactor if ((p = tab[i = (n - 1) & hash]) == null) //如果通过计算出来的下标处没有节点,就创建一个节点添加到table的该下标处,p为该下标处的第一个节点 tab[i] = newNode(hash, key, value, null); else {//该下标已经存在节点,就需要找到该节点 Node<K,V> e; K k;//e为要找的节点,k为要找到的节点的key if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))//如果该下标的第一个节点就是要找的节点 e = p; else if (p instanceof TreeNode)//如果第一个节点是树节点,则需要在树里面进行添加 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else {//如果是链表 for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) {//如果链表中没有key相同node存在,就根据key、value创建一个node,然后添加在链表的结尾 p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash);//如果整个链表的长度大于等于8了,就需要将链表转为树结构 break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))//如果找到了key相同的node结束循环。 break; p = e; } } if (e != null) { // existing mapping for key 确实找到了key相同的node V oldValue = e.value; if (!onlyIfAbsent || oldValue == null)//onlyIfAbsent == false 或者oldValue == null时对原来的 值进行覆盖 e.value = value; afterNodeAccess(e);//提供一个回调方法没有实现 return oldValue; } } ++modCount; if (++size > threshold)//如果添加后的元素个数大于threshold的值,则对hash表进行resize操作 resize(); afterNodeInsertion(evict);//回调方法没有实现 return null; } /** * 初始化或者将table的大小翻倍 ,如果table为null就按照默认的初始值DEFAULT_INITIAL_CAPACITY分配容量 * 另外、由于我们是将table的大小以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) {//如果以前的table长度大于0 if (oldCap >= MAXIMUM_CAPACITY) {//如果table长度大于等于MAXIMUM_CAPACITY,直接将存储极限设置为int的最大值 threshold = Integer.MAX_VALUE; return oldTab; } //如果table的长度小于MAXIMUM_CAPACITY并且大于DEFAULT_INITIAL_CAPACITY,就想table长度和存储极限都翻倍 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; // double threshold } else if (oldThr > 0) //创建HashMap的时候如果调用的是有参构造函数,容量就为计算的存储极限(在有参构造方法中就直接将threshold的 值设置为cap的值) newCap = oldThr; else {//用默认的构造方法创建HashMap,table的大小和存储极限都为默认值 newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) {//由于用有参构造函数创建的对象threshold的值其实是cap所以要重新计算一下threshold的值 float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr;//将存储极限设置为新值,并根据新的容量大小来创建新的table @SuppressWarnings({"rawtypes","unchecked"}) Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; table = newTab; if (oldTab != null) {//如果原来的table已经存在,要将原来的Node重新安排位置 for (int j = 0; j < oldCap; ++j) { Node<K,V> e; if ((e = oldTab[j]) != null) { oldTab[j] = null;//先将原来的table该位置的值设置为null if (e.next == null)//如果该下标处只有一个节点,则重新计算该节点的下标并赋值 newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode)//如果该下标的节点是树节点 ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); else { //各个节点如果是链表形式存储,那么就通过e.hash & oldCap)是否等于0来将原来的链表分为两个链表 //算法非常巧妙 Node<K,V> loHead = null, loTail = null; Node<K,V> hiHead = null, hiTail = null; Node<K,V> next; do { next = e.next; if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); if (loTail != null) {//其中lo这个链表还是放在原来的下标处, loTail.next = null; newTab[j] = loHead; } if (hiTail != null) {//hi链表放在(原来的下标+原来的cap) hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; } /** * 将桶转化为树结构 */ 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(); else if ((e = tab[index = (n - 1) & hash]) != null) { TreeNode<K,V> hd = null, tl = null; do { TreeNode<K,V> p = replacementTreeNode(e, null); if (tl == null) hd = p; else { p.prev = tl; tl.next = p; } tl = p; } while ((e = e.next) != null); if ((tab[index] = hd) != null) hd.treeify(tab); } } /** * 将map中的所有键值对插入到Map中 */ public void putAll(Map<? extends K, ? extends V> m) { putMapEntries(m, true); } /** * 删除key对应的节点 */ public V remove(Object key) { Node<K,V> e; return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value; } /** * Map.remove 和相关方法的 实现 * hash 是key的hash值 * key 为key * value 为value,主要是为了matchValue使用 * matchValue 如果为 true则只能在value也匹配的 时候才能移除 * movable 如果为false,移除了该Node不会移动其他节点 * */ 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;//Node用来存储找到的节点 if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1) & hash]) != null) {//table 不为null,table不为空,并且key对应的下标也有节点存在,才进行下一步 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;//如果该桶的第一个节点就是要找的节点 else if ((e = p.next) != null) { if (p instanceof TreeNode)//如果桶为树结构 node = ((TreeNode<K,V>)p).getTreeNode(hash, key); else {//如果是链表就循环遍历查找 do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { node = e; break; } p = e; } while ((e = e.next) != null); } } if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))) {//如果找到了该节点 if (node instanceof TreeNode)//树形结构的删除 ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable); else if (node == p)//如果是链表的第一个节点 tab[index] = node.next; else//如果是链表的其他节点 p.next = node.next; ++modCount; --size; afterNodeRemoval(node);//回调方法 return node; } } return null; } /** * 清空整个HashMap */ public void clear() { Node<K,V>[] tab; modCount++; if ((tab = table) != null && size > 0) { size = 0; for (int i = 0; i < tab.length; ++i) tab[i] = null; } } /** * 直接遍历然后查找 value值是否存在 */ public boolean containsValue(Object value) { Node<K,V>[] tab; V v; if ((tab = table) != null && size > 0) { for (int i = 0; i < tab.length; ++i) { for (Node<K,V> e = tab[i]; e != null; e = e.next) { if ((v = e.value) == value || (value != null && value.equals(v))) return true; } } } return false; } /** * 返回包含HashMap中键的Set集合,对HashMap的修改结果会反馈到Set中,反之亦然。 * 如果在对集合进行迭代的时候修改了HashMap(除了迭代器自己的 remove操作),迭代结果是未定义的 * 迭代器不支持add和addAll操作 */ public Set<K> keySet() { Set<K> ks = keySet; if (ks == null) { ks = new KeySet(); keySet = ks; } return ks; } final class KeySet extends AbstractSet<K> { public final int size() { return size; } public final void clear() { HashMap.this.clear(); } public final Iterator<K> iterator() { return new KeyIterator(); } public final boolean contains(Object o) { return containsKey(o); } public final boolean remove(Object key) { return removeNode(hash(key), key, null, false, true) != null; } public final Spliterator<K> spliterator() { return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0); } 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; for (int i = 0; i < tab.length; ++i) { for (Node<K,V> e = tab[i]; e != null; e = e.next) action.accept(e.key); } if (modCount != mc) throw new ConcurrentModificationException(); } } } /** * 和上面的KeySet一样,这个是获取Map中所有的value集合 * 同样对HashMap的修改也会反应到该集合上 */ public Collection<V> values() { Collection<V> vs = values; if (vs == null) { vs = new Values(); values = vs; } return vs; } final class Values extends AbstractCollection<V> { public final int size() { return size; } public final void clear() { HashMap.this.clear(); } public final Iterator<V> iterator() { return new ValueIterator(); } public final boolean contains(Object o) { return containsValue(o); } public final Spliterator<V> spliterator() { 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(); } } } /** * 返回HashMap中键值对集合,同样对HashMap的改变也会反应到集合上。反之亦然。 * 如果在对集合进行迭代时修改HashMap(除了通过迭代器自己的删除操作,或者通过迭代器返回的映射条目上的setValue操作),迭代的结果是未定义的。 * 集合支持元素删除,元素删除通过迭代器.remove, set从映射中删除对应的映射。它不支持add或addAll操作。 * */ public Set<Map.Entry<K,V>> entrySet() { Set<Map.Entry<K,V>> es; return (es = entrySet) == null ? (entrySet = new EntrySet()) : es; } final class EntrySet extends AbstractSet<Map.Entry<K,V>> { public final int size() { return size; } public final void clear() { HashMap.this.clear(); } public final Iterator<Map.Entry<K,V>> iterator() { return new EntryIterator(); } public final boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<?,?> e = (Map.Entry<?,?>) o; Object key = e.getKey(); Node<K,V> candidate = getNode(hash(key), key); return candidate != null && candidate.equals(e); } public final boolean remove(Object o) { if (o instanceof 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; } return false; } public final Spliterator<Map.Entry<K,V>> spliterator() { return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0); } 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; for (int i = 0; i < tab.length; ++i) { for (Node<K,V> e = tab[i]; e != null; e = e.next) action.accept(e); } if (modCount != mc) throw new ConcurrentModificationException(); } } } // Overrides of JDK8 Map extension methods 重写JDK8的扩展方法 //返回key对应的value,如果没有就返回defaultValue @Override public V getOrDefault(Object key, V defaultValue) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? defaultValue : e.value; } //添加键值对,如果key已经存在,不覆盖 @Override public V putIfAbsent(K key, V value) { return putVal(hash(key), key, value, true, true); } //要key和value都相等才会删除节点,并且将后面的节点往前移动(争对树桶) @Override public boolean remove(Object key, Object value) { return removeNode(hash(key), key, value, true, true) != null; } //只有在key和oldValue和HashMap中的完全相同的时候,才会用newValue来替换oldValue @Override public boolean replace(K key, V oldValue, V newValue) { Node<K,V> e; V v; if ((e = getNode(hash(key), key)) != null && ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) { e.value = newValue; afterNodeAccess(e); return true; } return false; } //用value替换原来的值 @Override public V replace(K key, V value) { Node<K,V> e; if ((e = getNode(hash(key), key)) != null) { V oldValue = e.value; e.value = value; afterNodeAccess(e); return oldValue; } return null; } /**如果key对应的value为null或者key不存在,就将key和用function计算出来value作为键值对保存起来 * map.computeIfAbsent("12", k -> k+"a") 如果12不存在或者value为null,就将12a作为value */ @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) n = (tab = resize()).length; if ((first = tab[i = (n - 1) & hash]) != null) { if (first instanceof TreeNode) old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key); else { Node<K,V> e = first; K k; do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { old = e; break; } ++binCount; } while ((e = e.next) != null); } V oldValue; if (old != null && (oldValue = old.value) != null) { afterNodeAccess(old); return oldValue; } } V v = mappingFunction.apply(key); if (v == null) { return null; } else if (old != null) { old.value = v; afterNodeAccess(old); return v; } else if (t != null) t.putTreeVal(this, tab, hash, key, v); else { tab[i] = newNode(hash, key, v, first); if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash); } ++modCount; ++size; afterNodeInsertion(true); return v; } /** * 如果key存在(key为null也算存在)且value值不为null的时候,对key-value进行替换 * map.computeIfPresent("12", (k,v) -> v+"a"); */ 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) { V v = remappingFunction.apply(key, oldValue); if (v != null) { e.value = v; afterNodeAccess(e); return v; } else removeNode(hash, key, null, false, true); } return null; } /** * 不管key是否存在,也不管value是否为null,都要替换value * hashMap.compute("13", (k,v) -> k+'a'); */ @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); } } V oldValue = (old == null) ? null : old.value; V v = remappingFunction.apply(key, oldValue); if (old != null) { if (v != null) { old.value = v; afterNodeAccess(old); } else removeNode(hash, key, null, false, true); } else if (v != null) { if (t != null) t.putTreeVal(this, tab, hash, key, v); else { tab[i] = newNode(hash, key, v, first); if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash); } ++modCount; ++size; afterNodeInsertion(true); } return v; } /** * 如果key不存在或者value为null,就将value赋值给键值对 * 如果key存在且value不为null,就通过后面的表达式来计算出新的value * 如果最后U计算出来的value值为null,就将key删除 * map.merge("12", "b", (v1,v2) -> v1+v2) */ @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); } } if (old != null) { V v; if (old.value != null) v = remappingFunction.apply(old.value, value); else v = value; if (v != null) { old.value = v; afterNodeAccess(old); } else removeNode(hash, key, null, false, true); return v; } if (value != null) { if (t != null) t.putTreeVal(this, tab, hash, key, value); else { tab[i] = newNode(hash, key, value, first); if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash); } ++modCount; ++size; afterNodeInsertion(true); } return value; } /** * 循环遍历HashMap,然后执行括号中的方法 * map.forEach((k,v) -> System.out.println(k+"-"+v)); */ @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; for (int i = 0; i < tab.length; ++i) { for (Node<K,V> e = tab[i]; e != null; e = e.next) action.accept(e.key, e.value); } if (modCount != mc) throw new ConcurrentModificationException(); } } /** * 通过括号中的表达式来匹配键值对,并将返回值替换原来的value * hashMap.replaceAll((k,v) -> { * if(Integer.parseInt(k)>=10){ * v = v+"a"; * } * return v; * }); *如果k的值大于等于10,就在value后面加a */ @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; 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); } } if (modCount != mc) throw new ConcurrentModificationException(); } } /* ------------------------------------------------------------ */ // Cloning and serialization /** * 浅克隆 */ @SuppressWarnings("unchecked") @Override public Object clone() { HashMap<K,V> result; try { result = (HashMap<K,V>)super.clone(); } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(e); } result.reinitialize(); result.putMapEntries(this, false); return result; } // These methods are also used when serializing HashSets final float loadFactor() { return loadFactor; } final int capacity() { return (table != null) ? table.length : (threshold > 0) ? threshold : DEFAULT_INITIAL_CAPACITY; } /** * 将HashMap写入流中年 */ private void writeObject(java.io.ObjectOutputStream s) throws IOException { int buckets = capacity(); // Write out the threshold, loadfactor, and any hidden stuff s.defaultWriteObject(); s.writeInt(buckets); s.writeInt(size); internalWriteEntries(s); } /** * 从流中读取HashMap对象 */ private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { // Read in the threshold (ignored), loadfactor, and any hidden stuff s.defaultReadObject(); reinitialize(); if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new InvalidObjectException("Illegal load factor: " + loadFactor); s.readInt(); // Read and ignore number of buckets int mappings = s.readInt(); // Read number of mappings (size) if (mappings < 0) throw new InvalidObjectException("Illegal mappings count: " + mappings); else if (mappings > 0) { // (if zero, use defaults) // Size the table using given load factor only if within // range of 0.25...4.0 float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f); float fc = (float)mappings / lf + 1.0f; int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ? DEFAULT_INITIAL_CAPACITY : (fc >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : tableSizeFor((int)fc)); float ft = (float)cap * lf; threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ? (int)ft : Integer.MAX_VALUE); @SuppressWarnings({"rawtypes","unchecked"}) Node<K,V>[] tab = (Node<K,V>[])new Node[cap]; table = tab; // Read the keys and values, and put the mappings in the 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); } } } /* ------------------------------------------------------------ */ // iterators 迭代器 abstract class HashIterator { Node<K,V> next; // next entry to return Node<K,V> current; // current entry int expectedModCount; // for fast-fail int index; // current slot HashIterator() { expectedModCount = modCount; Node<K,V>[] t = table; current = next = null; index = 0; if (t != null && size > 0) { // advance to first entry do {} while (index < t.length && (next = t[index++]) == null); } } public final boolean hasNext() { return next != null; } 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) { do {} while (index < t.length && (next = t[index++]) == null); } return e; } public final void remove() { Node<K,V> p = current; if (p == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); current = null; K key = p.key; removeNode(hash(key), key, null, false, false); expectedModCount = modCount; } } final class KeyIterator extends HashIterator implements Iterator<K> { public final K next() { return nextNode().key; } } final class ValueIterator extends HashIterator implements Iterator<V> { public final V next() { return nextNode().value; } } final class EntryIterator extends HashIterator implements Iterator<Map.Entry<K,V>> { public final Map.Entry<K,V> next() { return nextNode(); } } /* ------------------------------------------------------------ */ // spliterators 用多线程来并行迭代的迭代器,将整个HashMap分割成多个部分,然后分别迭代 //下面的KeySpliterator,ValueSpliterator,EntrySpliterator同理 static class HashMapSpliterator<K,V> { final HashMap<K,V> map; Node<K,V> current; // current node int index; // current index, modified on advance/split int fence; // one past last index int est; // size estimate int expectedModCount; // for comodification checks 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() { // initialize fence and size on first use int hi; if ((hi = fence) < 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(); // force init return (long) est; } } 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) { 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.key); p = p.next; } } while (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 = tab[index++]; else { K k = current.key; current = current.next; action.accept(k); 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; } } 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) { 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); 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 support 对于LinkedHashMapde 支持 /* * 以下受包保护的方法被设计为可由LinkedHashMap覆盖,但不被任何其他子类覆盖。 * 几乎所有其他内部方法也受包保护,但都声明为final,所以LinkedHashMap、视图类和HashSet都可以使用这些方法 * */ // Create a regular (non-tree) node Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) { return new Node<>(hash, key, value, next); } // For conversion from TreeNodes to plain nodes Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) { return new Node<>(p.hash, p.key, p.value, next); } // Create a tree bin node TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) { return new TreeNode<>(hash, key, value, next); } // For treeifyBin TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) { return new TreeNode<>(p.hash, p.key, p.value, next); } /** * Reset to initial default state. Called by clone and readObject. */ void reinitialize() { table = null; entrySet = null; keySet = null; values = null; modCount = 0; threshold = 0; size = 0; } // Callbacks to allow LinkedHashMap post-actions void afterNodeAccess(Node<K,V> p) { } void afterNodeInsertion(boolean evict) { } void afterNodeRemoval(Node<K,V> p) { } // Called only from writeObject, to ensure compatible ordering. 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) { for (Node<K,V> e = tab[i]; e != null; e = e.next) { s.writeObject(e.key); s.writeObject(e.value); } } } } /* ------------------------------------------------------------ */ // Tree bins 下面的是树结点相关的一些代码,比较难懂,以后再分析 /** * Entry for Tree bins. Extends LinkedHashMap.Entry (which in turn * extends Node) so can be used as extension of either regular or * linked node. */ static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> { TreeNode<K,V> parent; // red-black tree links TreeNode<K,V> left; TreeNode<K,V> right; TreeNode<K,V> prev; // needed to unlink next upon deletion boolean red; TreeNode(int hash, K key, V val, Node<K,V> next) { super(hash, key, val, next); } /** * Returns root of tree containing this node. */ final TreeNode<K,V> root() { for (TreeNode<K,V> r = this, p;;) { if ((p = r.parent) == null) return r; r = p; } } /** * Ensures that the given root is the first node of its bin. */ 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; TreeNode<K,V> first = (TreeNode<K,V>)tab[index]; if (root != first) { Node<K,V> rn; tab[index] = root; TreeNode<K,V> rp = root.prev; if ((rn = root.next) != null) ((TreeNode<K,V>)rn).prev = rp; if (rp != null) rp.next = rn; if (first != null) first.prev = root; root.next = first; root.prev = null; } assert checkInvariants(root); } } /** * Finds the node starting at root p with the given hash and key. * The kc argument caches comparableClassFor(key) upon first use * comparing keys. */ final TreeNode<K,V> find(int h, Object k, Class<?> kc) { TreeNode<K,V> p = this; do { int ph, dir; K pk; TreeNode<K,V> pl = p.left, pr = p.right, q; if ((ph = p.hash) > h) p = pl; else if (ph < h) p = pr; else if ((pk = p.key) == k || (k != null && k.equals(pk))) return p; else if (pl == null) p = pr; else if (pr == null) p = pl; else if ((kc != null || (kc = comparableClassFor(k)) != null) && (dir = compareComparables(kc, k, pk)) != 0) p = (dir < 0) ? pl : pr; else if ((q = pr.find(h, k, kc)) != null) return q; else p = pl; } while (p != null); return null; } /** * Calls find for root node. */ final TreeNode<K,V> getTreeNode(int h, Object k) { return ((parent != null) ? root() : this).find(h, k, null); } /** * Tie-breaking utility for ordering insertions when equal * hashCodes and non-comparable. We don't require a total * order, just a consistent insertion rule to maintain * equivalence across rebalancings. Tie-breaking further than * necessary simplifies testing a bit. */ static int tieBreakOrder(Object a, Object b) { int d; if (a == null || b == null || (d = a.getClass().getName(). compareTo(b.getClass().getName())) == 0) d = (System.identityHashCode(a) <= System.identityHashCode(b) ? -1 : 1); return d; } /** * Forms tree of the nodes linked from this node. * @return root of tree */ 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; x.left = x.right = null; if (root == null) { x.parent = null; x.red = false; root = x; } else { K k = x.key; int h = x.hash; Class<?> kc = null; for (TreeNode<K,V> p = root;;) { int dir, ph; K pk = p.key; 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); 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); break; } } } } moveRootToFront(tab, root); } /** * Returns a list of non-TreeNodes replacing those linked from * this node. */ 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) { Node<K,V> p = map.replacementNode(q, null); if (tl == null) hd = p; else tl.next = p; tl = p; } return hd; } /** * Tree version of 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; for (TreeNode<K,V> p = root;;) { int dir, ph; K pk; if ((ph = p.hash) > h) dir = -1; else if (ph < h) dir = 1; else if ((pk = p.key) == k || (k != null && k.equals(pk))) return p; else if ((kc == null && (kc = comparableClassFor(k)) == null) || (dir = compareComparables(kc, k, pk)) == 0) { if (!searched) { TreeNode<K,V> q, ch; searched = true; if (((ch = p.left) != null && (q = ch.find(h, k, kc)) != null) || ((ch = p.right) != null && (q = ch.find(h, k, kc)) != null)) return q; } dir = tieBreakOrder(k, pk); } TreeNode<K,V> xp = p; if ((p = (dir <= 0) ? p.left : p.right) == null) { Node<K,V> xpn = xp.next; TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn); if (dir <= 0) xp.left = x; else xp.right = x; xp.next = x; x.parent = x.prev = xp; if (xpn != null) ((TreeNode<K,V>)xpn).prev = x; moveRootToFront(tab, balanceInsertion(root, x)); return null; } } } /** * Removes the given node, that must be present before this call. * This is messier than typical red-black deletion code because we * cannot swap the contents of an interior node with a leaf * successor that is pinned by "next" pointers that are accessible * independently during traversal. So instead we swap the tree * linkages. If the current tree appears to have too few nodes, * the bin is converted back to a plain bin. (The test triggers * somewhere between 2 and 6 nodes, depending on tree structure). */ 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; TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl; TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev; if (pred == null) tab[index] = first = succ; else pred.next = succ; if (succ != null) succ.prev = pred; if (first == null) return; if (root.parent != null) root = root.root(); if (root == null || root.right == null || (rl = root.left) == null || rl.left == null) { tab[index] = first.untreeify(map); // too small 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) // find successor s = sl; boolean c = s.red; s.red = p.red; p.red = c; // swap colors TreeNode<K,V> sr = s.right; TreeNode<K,V> pp = p.parent; if (s == pr) { // p was s's direct parent 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) { // detach 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); } /** * Splits nodes in a tree bin into lower and upper tree bins, * or untreeifies if now too small. Called only from resize; * see above discussion about split bits and indices. * * @param map the map * @param tab the table for recording bin heads * @param index the index of the table being split * @param bit the bit of hash to split on */ final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) { TreeNode<K,V> b = this; // Relink into lo and hi lists, preserving order 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 is already treeified) 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); } } } /* ------------------------------------------------------------ */ // Red-black tree methods, all adapted from CLR 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 { // symmetric 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; } } } } } /** * Recursive invariant check */ 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的存储结构?
HashMap底层是用一个数组table来存储键值对的Node,table的每个下标称为一个桶,通过key计算出来的hash值和table的长度计算出来下标值,键值对就存储在table的该下标处的桶中如果计算得到的下标相同就会被存入到同一个桶里面,以单链表的形式来进行存储,如果链表太长就会以tree的形式来对Node进行存储。
2、HashMap的初始容量、最大容量和扩容因子是多少?
如果使用默认的构造函数创建对象,那初始容量为16,默认的扩容因子为0.75。HashMap的容量必须是2的整数次幂。
/** * 默认的初始化table的大小16,必须是2的整数次幂 */ static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 /** * 最大容量大小,必须是小于1<<30的2的整数次幂 */ static final int MAXIMUM_CAPACITY = 1 << 30; /** * 默认的加载因子 */ static final float DEFAULT_LOAD_FACTOR = 0.75f;
3、HashMap在什么情况下会扩容?扩大到多少?
通过构造函数创建HashMap的对象的时候,底层是数组table为null。在加入第一个键值对的时候会调用resize()方法来利用DEFAULT_INITIAL_CAPACITY 和DEFAULT_LOAD_FACTOR 或者自己传入的 参数来创建一Node数组。如果是无参构造函数,那么该数组的大小就是DEFAULT_INITIAL_CAPACITY 。存储的极限就是DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR 。
如果不是第一次加入键值对,如果加入键值对后的键值对数量size大于存储的极限threshold(当前容量*当前加载因子),那么也需要调用resize()方法来进行扩容。此时会将容量和存储极限扩展为原来的两倍。也就是创建新的数组,然后将原来的键值对重新分配位置。
第三种情况就是如果有的桶中的Node数量已经大于等于TREEIFY_THRESHOLD(8)时,但是整个HashMap的桶数量还没有达到MIN_TREEIFY_CAPACITY(64)时,就调用resize()方法来进行扩容。
4、HashMap是如何确定新加入的键值对要存储的位置的?
通过hash方法来获取key的hash值,然后(n - 1) & hash来获取key对应的下标值,其中n为当前table的大小。这样就能找到当前键值对需要加入的桶,如果该桶是链表结构就直接加载链表的结尾。
如果该桶是树形结构,就需要对hash值进行比较来确定键值对的位置,如果hash值相同就比较key之间是可比较的,如果是不可比较的就比较当前节点和指定节点key的类名,就这样一步步的确定key锁对应的位置。
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
5、什么时候会将桶由链表结构变为树形结构,又何时变回来
在桶中的Node数量大于等于TREEIFY_THRESHOLD且table大于等于MIN_TREEIFY_CAPACITY的时候就会转为树结构。当桶中的节点数量小于等于MIN_TREEIFY_CAPACITY时,就需要将树型转化为链表结构。
6、线程安全问题、是否可以存null键和null值
HashMap是线程不安全的,所以在多线程环境下使用的时候要小心。它和HashTable不同,它可以存储null键和null值。