jdk1.8-HashMap源码学习

说明:
链表转红黑树的条件是:
tab.length >= MIN_TREEIFY_CAPACITY(64)且链表中节点个数大于8,调用的是treeifyBin()
红黑树转链表的条件是:链表中节点个数小于6,调用untreeify()
/**
 * HashMap基于Map接口实现。与HashTable主要区别为不支持同步和允许null作为key和value
 * HashMap非线程安全,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致
 * 满足线程安全,可以用 Collections的synchronizedMap方法或使用ConcurrentHashMap
 * 在JDK1.6中,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里
 * 但是当hash值相等的元素较多时,通过key值依次查找的效率较低。
 * 而JDK1.8中,HashMap采用数组+链表+红黑树实现,当链表长度超过阈值8时,将链表转换为红黑树,这样大大减少了查找时间
 * 原本Map.Entry接口的实现类Entry改名为了Node。转化为红黑树时改用另一种实现TreeNode
 */
public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable {
   
    //默认初始容量16(槽或者叫桶的个数),实际容量必须是2的整数次幂
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //16
 
    //最大容量(小于2的30次方,传入容量过大将被这个值替换)
    static final int MAXIMUM_CAPACITY = 1 << 30;
 
    //默认负载因子
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    //JDK1.8 新加,链表转红黑树的阈值,链表长度大于8时转
    static final int TREEIFY_THRESHOLD = 8;
     
    //JDK1.8 新加,红黑树转链表的阈值    
    static final int UNTREEIFY_THRESHOLD = 6;
 
    /**
     * 桶可能被转化为树形结构的最小容量。当哈希表的大小超过这个阈值,才会把链式结构转化成树型结构,否则仅采取扩容来尝试减少冲突。
     * 应该至少4*TREEIFY_THRESHOLD来避免扩容和树形结构化之间的冲突。
     */
    static final int MIN_TREEIFY_CAPACITY = 64;
   
    //JDK1.6用Entry描述键值对,JDK1.8中用Node代替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; } } /* ---------------- Static utilities -------------- */ /** * HashMap中键值对的存储形式为链表节点,hashCode相同的节点(位于同一个桶)用链表组织 * hash方法分为三步: * 1.取key的hashCode * 2.key的hashCode高16位异或低16位 * 3.将第一步和第二步得到的结果进行取模运算。 */ static final int hash(Object key) { int h; //h >>> 16表示对h无符号右移16位,高位补0,然后h与h >>> 16按位异或 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } /** * 返回>=cap的最小2的n次幂 */ static final int tableSizeFor(int cap) { //先移位再或运算,最终保证返回值是2的整数幂 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 -------------- */ //哈希桶数组,分配的时候,table的长度总是2的幂 transient Node<K, V>[] table; //HashMap将数据转换成set的另一种存储形式,这个变量主要用于迭代功能 transient Set<Map.Entry<K, V>> entrySet; //实际存储的数量,则HashMap的size()方法,实际返回的就是这个值,isEmpty()也是判断该值是否为0 transient int size; //hashmap结构被改变的次数,fail-fast机制 transient int modCount; //扩容阈值threshold=table.length*loadFactor int threshold; //负载因子,当前table长度下的扩容阈值 final float loadFactor; /* ---------------- Public operations -------------- */ /** * 使用指定的初始化容量initialCapacity 和loadFactor构造一个空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); } /** * 使用指定的初始化容量initialCapacity构造一个空HashMap */ public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } /** * 使用默认初始化容量和加载因子构造一个空HashMap */ public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; } /** * Map.putAll and Map constructor的实现需要的方法 * 将m的键值对插入本map中 * * @param m 指定的map * @param evict 初始化map时使用false,否则使用true */ final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) { int s = m.size(); //如果参数map不为空 if (s > 0) { // 判断table是否已经初始化 if (table == null) { // pre-size // 未初始化,s为m的实际元素个数 float ft = ((float) s / loadFactor) + 1.0F; int t = ((ft < (float) MAXIMUM_CAPACITY) ? (int) ft : MAXIMUM_CAPACITY); // 计算得到的t大于阈值,则初始化阈值 if (t > threshold) //根据容量初始化临界值 threshold = tableSizeFor(t); // 已初始化,并且m元素个数大于阈值,进行扩容处理 } else if (s > threshold) //扩容处理 resize(); // 将m中的所有元素添加至HashMap中 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); } } } /** * 返回map中键值对映射的个数 */ public int size() { return size; } /** * 如果map中没有键值对映射,返回true */ public boolean isEmpty() { return size == 0; } /** * 返回指定的key映射的value,如果value为null,则返回null * get可以分为三个步骤: * 1.通过hash(Object key)方法计算key的哈希值hash。 * 2.通过getNode( int hash, Object key)方法获取node。 * 3.如果node为null,返回null,否则返回node.value。 * * @see #put(Object, Object) */ public V get(Object key) { Node<K, V> e; //根据key及其hash值查询node节点,如果存在,则返回该节点的value值 return (e = getNode(hash(key), key)) == null ? null : e.value; } /** * 根据key的哈希值和key获取对应的节点 * getNode可分为以下几个步骤: * 1.如果哈希表为空,或key对应的桶为空,返回null * 2.如果桶中的第一个节点就和指定参数hash和key匹配上了,返回这个节点。 * 3.如果桶中的第一个节点没有匹配上,而且有后续节点 * 3.1如果当前的桶采用红黑树,则调用红黑树的get方法去获取节点 * 3.2如果当前的桶不采用红黑树,即桶中节点结构为链式结构,遍历链表,直到key匹配 * 4.找到节点返回null,否则返回null。 * * @param hash 指定参数key的哈希值 * @param key 指定参数key * @return 返回node,如果没有则返回null */ final Node<K, V> getNode(int hash, Object key) { Node<K, V>[] tab; Node<K, V> first, e; int n; K k; //如果哈希表不为空,而且key对应的桶上不为空 if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { //如果桶中的第一个节点就和指定参数hash和key匹配上了 if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) //返回桶中的第一个节点 return first; //如果桶中的第一个节点没有匹配上,而且有后续节点 if ((e = first.next) != null) { //如果当前的桶采用红黑树,则调用红黑树的get方法去获取节点 if (first instanceof TreeNode) return ((TreeNode<K, V>) first).getTreeNode(hash, key); //如果当前的桶不采用红黑树,即桶中节点结构为链式结构 do { //遍历链表,直到key匹配 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } //如果哈希表为空,或者没有找到节点,返回null return null; } /** * 如果map中含有key为指定参数key的键值对,返回true * * @param key 指定参数key * @return 如果map中含有key为指定参数key的键值对,返回true * key. */ public boolean containsKey(Object key) { return getNode(hash(key), key) != null; } /** * 将指定参数key和指定参数value插入map中,如果key已经存在,那就替换key对应的value * put(K key, V value)可以分为三个步骤: * 1.通过hash(Object key)方法计算key的哈希值。 * 2.通过putVal(hash(key), key, value, false, true)方法实现功能。 * 3.返回putVal方法返回的结果。 * * @param key 指定key * @param value 指定value * @return 如果value被替换,则返回旧的value,否则返回null。当然,可能key对应的value就是null */ public V put(K key, V value) { // 倒数第二个参数false:表示允许旧值替换 // 最后一个参数true:表示HashMap不处于创建模式 return putVal(hash(key), key, value, false, true); } /** * Map.put和其他相关方法的实现需要的方法 * putVal方法可以分为下面的几个步骤: * 1.如果哈希表为空,调用resize()创建一个哈希表。 * 2.如果指定参数hash在表中没有对应的桶,即为没有碰撞,直接将键值对插入到哈希表中即可。 * 3.如果有碰撞,遍历桶,找到key映射的节点 * 3.1桶中的第一个节点就匹配了,将桶中的第一个节点记录起来。 * 3.2如果桶中的第一个节点没有匹配,且桶中结构为红黑树,则调用红黑树对应的方法插入键值对。 * 3.3如果不是红黑树,那么就肯定是链表。遍历链表,如果找到了key映射的节点,就记录这个节点,退出循环。如果没有找到,在链表尾部插入节点。插入后,如果链的长度大于TREEIFY_THRESHOLD这个临界值,则使用treeifyBin方法把链表转为红黑树。 * 4.如果找到了key映射的节点,且节点不为null * 4.1记录节点的vlaue。 * 4.2如果参数onlyIfAbsent为false,或者oldValue为null,替换value,否则不替换。 * 4.3返回记录下来的节点的value。 * 5.如果没有找到key映射的节点(2、3步中讲了,这种情况会插入到hashMap中),插入节点后size会加1,这时要检查size是否大于临界值threshold,如果大于会使用resize方法进行扩容。 * * @param hash 指定参数key的哈希值 * @param key 指定参数key * @param value 指定参数value * @param onlyIfAbsent 如果为true,即使指定参数key在map中已经存在,也不会替换value * @param evict 如果为false,数组table在创建模式中 * @return 如果value被替换,则返回旧的value,否则返回null。当然,可能key对应的value就是null。 */ final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K, V>[] tab; Node<K, V> p; int n, i; //如果哈希表为空,调用resize()创建一个哈希表,并用变量n记录哈希表长度 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; /** * 如果指定参数hash在表中没有对应的桶,即为没有碰撞 * Hash函数,(n - 1) & hash 计算key将被放置的槽位 * (n - 1) & hash 本质上是hash % n,位运算更快 */ if ((p = tab[i = (n - 1) & hash]) == null) //直接将键值对插入到map中即可 tab[i] = newNode(hash, key, value, null); else {// 桶中已经存在元素 Node<K, V> e; K k; // 比较桶中第一个元素(数组中的结点)的hash值相等,key相等 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) // 将第一个元素赋值给e,用e来记录 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) { p.next = newNode(hash, key, value, null); // 检查链表长度是否达到阈值,达到将该槽位节点组织形式转为红黑树 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } // 链表节点的<key, value>与put操作<key, value>相同时,不做重复操作,跳出循环 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } // 找到或新建一个key和hashCode与插入元素相等的键值对,进行put操作 if (e != null) { // existing mapping for key // 记录e的value V oldValue = e.value; /** * onlyIfAbsent为false或旧值为null时,允许替换旧值 * 否则无需替换 */ if (!onlyIfAbsent || oldValue == null) e.value = value; // 访问后回调 afterNodeAccess(e); // 返回旧值 return oldValue; } } // 更新结构化修改信息 ++modCount; // 键值对数目超过阈值时,进行rehash if (++size > threshold) resize(); // 插入后回调 afterNodeInsertion(evict); return null; } /** * 对table进行初始化或者扩容。 * 如果table为null,则对table进行初始化 * 如果对table扩容,因为每次扩容都是翻倍,与原来计算(n-1)&hash的结果相比,节点要么就在原来的位置,要么就被分配到“原位置+旧容量”这个位置 * resize的步骤总结为: * 1.计算扩容后的容量,临界值。 * 2.将hashMap的临界值修改为扩容后的临界值 * 3.根据扩容后的容量新建数组,然后将hashMap的table的引用指向新数组。 * 4.将旧数组的元素复制到table中。 * * @return the table */ final Node<K, V>[] resize() { //新建oldTab数组保存扩容前的数组table Node<K, V>[] oldTab = table; //获取原来数组的长度 int oldCap = (oldTab == null) ? 0 : oldTab.length; //原来数组扩容的临界值 int oldThr = threshold; int newCap, newThr = 0; //如果扩容前的容量 > 0 if (oldCap > 0) { //如果原来的数组长度大于最大值(2^30) if (oldCap >= MAXIMUM_CAPACITY) { //扩容临界值提高到正无穷 threshold = Integer.MAX_VALUE; //无法进行扩容,返回原来的数组 return oldTab; //如果现在容量的两倍小于MAXIMUM_CAPACITY且现在的容量大于DEFAULT_INITIAL_CAPACITY } else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) //临界值变为原来的2倍 newThr = oldThr << 1; } else if (oldThr > 0) //如果旧容量 <= 0,而且旧临界值 > 0 //数组的新容量设置为老数组扩容的临界值 newCap = oldThr; else { //如果旧容量 <= 0,且旧临界值 <= 0,新容量扩充为默认初始化容量,新临界值为DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY newCap = DEFAULT_INITIAL_CAPACITY;//新数组初始容量设置为默认值 newThr = (int) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//计算默认容量下的阈值 } // 计算新的resize上限 if (newThr == 0) {//在当上面的条件判断中,只有oldThr > 0成立时,newThr == 0 //ft为临时临界值,下面会确定这个临界值是否合法,如果合法,那就是真正的临界值 float ft = (float) newCap * loadFactor; //当新容量< MAXIMUM_CAPACITY且ft < (float)MAXIMUM_CAPACITY,新的临界值为ft,否则为Integer.MAX_VALUE newThr = (newCap < MAXIMUM_CAPACITY && ft < (float) MAXIMUM_CAPACITY ? (int) ft : Integer.MAX_VALUE); } //将扩容后hashMap的临界值设置为newThr threshold = newThr; //创建新的table,初始化容量为newCap @SuppressWarnings({"rawtypes", "unchecked"}) Node<K, V>[] newTab = (Node<K, V>[]) new Node[newCap]; //修改hashMap的table为新建的newTab table = newTab; //如果旧table不为空,将旧table中的元素复制到新的table中 if (oldTab != null) { //遍历旧哈希表的每个桶,将旧哈希表中的桶复制到新的哈希表中 for (int j = 0; j < oldCap; ++j) { Node<K, V> e; //如果旧桶不为null,使用e记录旧桶 if ((e = oldTab[j]) != null) { //将旧桶置为null oldTab[j] = null; //如果旧桶中只有一个node if (e.next == null) //将e也就是oldTab[j]放入newTab中e.hash & (newCap - 1)的位置 newTab[e.hash & (newCap - 1)] = e; //如果旧桶中的结构为红黑树 else if (e instanceof TreeNode) //将树中的node分离 ((TreeNode<K, V>) e).split(this, newTab, j, oldCap); else { //如果旧桶中的结构为链表,链表重排,jdk1.8做的一系列优化 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 {// 原索引+oldCap if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); // 原索引放到bucket里 if (loTail != null) { loTail.next = null; newTab[j] = loHead; } // 原索引+oldCap放到bucket里 if (hiTail != null) { 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; //如果桶数组table为空,或者桶数组table的长度小于MIN_TREEIFY_CAPACITY,不符合转化为红黑树的条件 if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) //扩容 resize(); //如果符合转化为红黑树的条件,而且hash对应的桶不为null else if ((e = tab[index = (n - 1) & hash]) != null) { // 红黑树的头、尾节点 TreeNode<K, V> hd = null, tl = null; //遍历链表 do { //替换链表node为树node,建立双向链表 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中的所有键值对映射插入到hashMap中,如果有碰撞,则覆盖value。 * * @param m 参数map * @throws NullPointerException 如果map为null */ public void putAll(Map<? extends K, ? extends V> m) { putMapEntries(m, true); } /** * 删除hashMap中key映射的node * remove方法的实现可以分为三个步骤: * 1.通过 hash(Object key)方法计算key的哈希值。 * 2.通过 removeNode 方法实现功能。 * 3.返回被删除的node的value。 * * @param key 参数key * @return 如果没有映射到node,返回null,否则返回对应的value */ public V remove(Object key) { Node<K, V> e; //根据key来删除node。removeNode方法的具体实现在下面 return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value; } /** * Map.remove和相关方法的实现需要的方法 * removeNode方法的步骤总结为: * 1.如果数组table为空或key映射到的桶为空,返回null。 * 2.如果key映射到的桶上第一个node的就是要删除的node,记录下来。 * 3.如果桶内不止一个node,且桶内的结构为红黑树,记录key映射到的node。 * 4.桶内的结构不为红黑树,那么桶内的结构就肯定为链表,遍历链表,找到key映射到的node,记录下来。 * 5.如果被记录下来的node不为null,删除node,size-1被删除。 * 6.返回被删除的node。 * * @param hash key的哈希值 * @param key key的哈希值 * @param value 如果 matchValue 为true,则value也作为确定被删除的node的条件之一,否则忽略 * @param matchValue 如果为true,则value也作为确定被删除的node的条件之一 * @param movable 如果为false,删除node时不会删除其他node * @return 返回被删除的node,如果没有node被删除,则返回null(针对红黑树的删除方法) */ 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; //如果数组table不为空且key映射到的桶不为空 if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1) & hash]) != null) { Node<K, V> node = null, e; K k; V v; //如果桶上第一个node的就是要删除的node if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) //记录桶上第一个node node = p; else if ((e = p.next) != null) {//如果桶内不止一个node //如果桶内的结构为红黑树 if (p instanceof TreeNode) //记录key映射到的node node = ((TreeNode<K, V>) p).getTreeNode(hash, key); else {//如果桶内的结构为链表 do {//遍历链表,找到key映射到的node if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { //记录key映射到的node node = e; break; } p = e; } while ((e = e.next) != null); } } //如果得到的node不为null且(matchValue为false||node.value和参数value匹配) if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))) { //如果桶内的结构为红黑树 if (node instanceof TreeNode) //使用红黑树的删除方法删除node ((TreeNode<K, V>) node).removeTreeNode(this, tab, movable); else if (node == p)//如果桶的第一个node的就是要删除的node //删除node tab[index] = node.next; else//如果桶内的结构为链表,使用链表删除元素的方式删除node p.next = node.next; ++modCount;//结构性修改次数+1 --size;//哈希表大小-1 afterNodeRemoval(node); return node;//返回被删除的node } } return null;//如果数组table为空或key映射到的桶为空,返回null。 } /** * 删除map中所有的键值对 */ 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; } } /** * 如果hashMap中的键值对有一对或多对的value为参数value,返回true * * @param value 参数value * @return 如果hashMap中的键值对有一对或多对的value为参数value,返回true */ public boolean containsValue(Object value) { Node<K, V>[] tab; V v; if ((tab = table) != null && size > 0) { //遍历数组table for (int i = 0; i < tab.length; ++i) { //遍历桶中的node 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中所有key的视图。 * 改变hashMap会影响到set,反之亦然。 * 如果当迭代器迭代set时,hashMap被修改(除非是迭代器自己的remove()方法),迭代器的结果是不确定的。 * set支持元素的删除,通过Iterator.remove、Set.remove、removeAll、retainAll、clear操作删除hashMap中对应的键值对。 * 不支持add和addAll方法。 * * @return 返回hashMap中所有key的set视图 */ public Set<K> keySet() { Set<K> ks = keySet; if (ks == null) { ks = new KeySet(); keySet = ks; } return ks; } /** * 内部类KeySet */ 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(); } } } /** * 返回hashMap中所有value的collection视图 * 改变hashMap会改变collection,反之亦然。 * 如果当迭代器迭代collection时,hashMap被修改(除非是迭代器自己的remove()方法),迭代器的结果是不确定的。 * collection支持元素的删除,通过Iterator.remove、Collection.remove、removeAll、retainAll、clear操作删除hashMap中对应的键值对。 * 不支持add和addAll方法。 * * @return 返回hashMap中所有key的collection视图 */ public Collection<V> values() { Collection<V> vs = values; if (vs == null) { vs = new Values(); values = vs; } return vs; } /** * 内部类Values */ 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中所有键值对的set视图 * 改变hashMap会影响到set,反之亦然。 * 如果当迭代器迭代set时,hashMap被修改(除非是迭代器自己的remove()方法),迭代器的结果是不确定的。 * set支持元素的删除,通过Iterator.remove、Set.remove、removeAll、retainAll、clear操作删除hashMap中对应的键值对。 * 不支持add和addAll方法。 * * @return 返回hashMap中所有键值对的set视图 */ public Set<Map.Entry<K, V>> entrySet() { Set<Map.Entry<K, V>> es; return (es = entrySet) == null ? (entrySet = new EntrySet()) : es; } /** * 内部类EntrySet */ 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(); } } } // JDK8重写的方法 /** * 通过key映射到对应node,如果没映射到则返回默认值defaultValue * * @param key * @param defaultValue * @return key映射到对应的node,如果没映射到则返回默认值defaultValue */ @Override public V getOrDefault(Object key, V defaultValue) { Node<K, V> e; return (e = getNode(hash(key), key)) == null ? defaultValue : e.value; } /** * 在hashMap中插入参数key和value组成的键值对,如果key在hashMap中已经存在,不替换value * * @param key * @param value * @return 如果key在hashMap中不存在,返回旧value */ @Override public V putIfAbsent(K key, V value) { return putVal(hash(key), key, value, true, true); } /** * 删除hashMap中key为参数key,value为参数value的键值对。如果桶中结构为树,则级联删除 * * @param key * @param value * @return 删除成功,返回true */ @Override public boolean remove(Object key, Object value) { return removeNode(hash(key), key, value, true, true) != null; }/* ------------------------------------------------------------ */ // Tree bins /** * JDK1.8新增,用来支持桶的红黑树结构实现 * 性质1. 节点是红色或黑色。 * 性质2. 根是黑色。 * 性质3. 所有叶子都是黑色(叶子是NIL节点)。 * 性质4. 每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。) * 性质5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。 */ 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; //节点的前一个节点 boolean red; //true表示红节点,false表示黑节点 TreeNode(int hash, K key, V val, Node<K, V> next) { super(hash, key, val, next); }/** * 将链表转为二叉树 * * @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); } /** * 将二叉树转为链表 */ 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; } /** * 将结点太多的桶分割 * * @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); } } } }
源码分析过程摘自:
武培轩原创  https://blog.nowcoder.net/n/8b4547bd722b4f099865fbb93eec6692