publicclassHashMap<K,V>extendsAbstractMap<K,V>implementsMap<K,V>,Cloneable,Serializable{/**
* The default initial capacity - MUST be a power of two. * mjy:默认table大小
*/staticfinalintDEFAULT_INITIAL_CAPACITY=1<<4;// aka 16 /**
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments. *
* MUST be a power of two <= 1<<30.
* mjy:table最大长度
*/staticfinalintMAXIMUM_CAPACITY=1<<30;/**
* The load factor used when none specified in constructor.
* mjy:默认负载因子大小
*/staticfinalfloatDEFAULT_LOAD_FACTOR=0.75f;/**
* The bin count threshold for using a tree rather than list for a
* bin. Bins are converted to trees when adding an element to a
* bin with at least this many nodes. The value must be greater
* than 2 and should be at least 8 to mesh with assumptions in
* tree removal about conversion back to plain bins upon * shrinkage.
* mjy:链表树化阈值
*/staticfinalintTREEIFY_THRESHOLD=8;/**
* The bin count threshold for untreeifying a (split) bin during a
* resize operation. Should be less than TREEIFY_THRESHOLD, and at
* most 6 to mesh with shrinkage detection under removal.
* mjy:树降级为链表的阈值
*/staticfinalintUNTREEIFY_THRESHOLD=6;/**
* The smallest table capacity for which bins may be treeified.
* (Otherwise the table is resized if too many nodes in a bin.)
* Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
* between resizing and treeification thresholds.
* mjy:树化的另一个参数,当哈希表中的所有参数超过64,才会允许树化
*/staticfinalintMIN_TREEIFY_CAPACITY=64;/**
* Basic hash bin node, used for most entries. (See below for
* TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
* mjy:每个键值对通过这个Node存储
*/staticclassNode<K,V>implementsEntry<K,V>{// mjy:记录键的哈希值 finalinthash;// mjy:存储键 finalKkey;// mjy:存储值 Vvalue;// mjy:指向下一个节点,形成链表,处理哈希冲突 Node<K,V>next;Node(inthash,Kkey,Vvalue,Node<K,V>next){this.hash=hash;this.key=key;this.value=value;this.next=next;}publicfinalKgetKey(){returnkey;}publicfinalVgetValue(){returnvalue;}publicfinalStringtoString(){returnkey+"="+value;}publicfinalinthashCode(){returnObjects.hashCode(key)^Objects.hashCode(value);}publicfinalVsetValue(VnewValue){VoldValue=value;value=newValue;returnoldValue;}publicfinalbooleanequals(Objecto){if(o==this)returntrue;if(oinstanceofMap.Entry){Entry<?,?>e=(Entry<?,?>)o;if(Objects.equals(key,e.getKey())&&Objects.equals(value,e.getValue()))returntrue;}returnfalse;}}/* ---------------- Fields -------------- *//**
* The table, initialized on first use, and resized as
* necessary. When allocated, length is always a power of two.
* (We also tolerate length zero in some operations to allow
* bootstrapping mechanics that are currently not needed.)
* mjy:哈希表,初始化时机?
*/transientNode<K,V>[]table;/**
* Holds cached entrySet(). Note that AbstractMap fields are used
* for keySet() and values().
*/transientSet<Entry<K,V>>entrySet;/**
* The number of key-value mappings contained in this map.
* mjy:当前哈希表中元素个数
*/transientintsize;/**
* The number of times this HashMap has been structurally modified
* Structural modifications are those that change the number of mappings in
* the HashMap or otherwise modify its internal structure (e.g.,
* rehash). This field is used to make iterators on Collection-views of
* the HashMap fail-fast. (See ConcurrentModificationException).
* mjy:当前哈希表结构修改次数
*/transientintmodCount;/**
* The next size value at which to resize (capacity * load factor).
* mjy:扩容阈值,当哈希表中的元素超过阈值,出发扩容
* @serial
*/// (The javadoc description is true upon serialization. // Additionally, if the table array has not been allocated, this // field holds the initial array capacity, or zero signifying // DEFAULT_INITIAL_CAPACITY.) intthreshold;/**
* The load factor for the hash table.
* mjy:负载因子,通过负载因子计算阈值, threshold = capacity * loadFactor
* @serial
*/finalfloatloadFactor;}
/* ---------------- Public operations -------------- *//**
* Constructs an empty <tt>HashMap</tt> with the specified initial
* capacity and load factor.
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @throws IllegalArgumentException if the initial capacity is negative
* or the load factor is nonpositive */publicHashMap(intinitialCapacity,floatloadFactor){// mjy:一些校验,capacity必须大于0,最大值max if(initialCapacity<0)thrownewIllegalArgumentException("Illegal initial capacity: "+initialCapacity);if(initialCapacity>MAXIMUM_CAPACITY)initialCapacity=MAXIMUM_CAPACITY;// mjy:loadFactor必须大于0 if(loadFactor<=0||Float.isNaN(loadFactor))thrownewIllegalArgumentException("Illegal load factor: "+loadFactor);this.loadFactor=loadFactor;this.threshold=tableSizeFor(initialCapacity);}/**
* Constructs an empty <tt>HashMap</tt> with the specified initial
* capacity and the default load factor (0.75).
*
* @param initialCapacity the initial capacity.
* @throws IllegalArgumentException if the initial capacity is negative.
*/publicHashMap(intinitialCapacity){this(initialCapacity,DEFAULT_LOAD_FACTOR);}/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
*/publicHashMap(){this.loadFactor=DEFAULT_LOAD_FACTOR;// all other fields defaulted }/**
* Constructs a new <tt>HashMap</tt> with the same mappings as the
* specified <tt>Map</tt>. The <tt>HashMap</tt> is created with
* default load factor (0.75) and an initial capacity sufficient to
* hold the mappings in the specified <tt>Map</tt>.
*
* @param m the map whose mappings are to be placed in this map
* @throws NullPointerException if the specified map is null
*/publicHashMap(Map<?extendsK,?extendsV>m){this.loadFactor=DEFAULT_LOAD_FACTOR;putMapEntries(m,false);}
/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/publicVput(Kkey,Vvalue){returnputVal(hash(key),key,value,false,true);}/**
* Implements Map.put and related methods
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
* mjy:
* hash:键的哈希值,通常通过hash()方法计算得来。
* key:要插入的键
* value:要插入的值
* onlyIfAbsent:如果为 true,表示当键存在时不覆盖原有值(即只在没有映射的情况下插入)
* evict:是否在某些情况下触发后续处理(一般用于LinkedHashMap的操作)
*/finalVputVal(inthash,Kkey,Vvalue,booleanonlyIfAbsent,booleanevict){/**
* mjy:
* tab:存储键值对的哈希表(数组)
* p:当前哈希表的某个索引位置的第一个节点(即链表或红黑树的头节点)
* n:哈希表(数组)长度
* i:当前键应该插入的数组位置
*/Node<K,V>[]tab;Node<K,V>p;intn,i;// mjy:哈希表未初始化或者长度为0,调用resize()进行初始化(延迟初始化) if((tab=table)==null||(n=tab.length)==0)n=(tab=resize()).length;// mjy:p为当前节点,i = (n - 1) & hash 为当前键应该插入的数组位置(即索引i) if((p=tab[i=(n-1)&hash])==null)// mjy:当前索引处元素为空,创建新节点并插入 tab[i]=newNode(hash,key,value,null);else{/**
* mjy:当前索引处不为空
* e:临时存储当前元素
*/Node<K,V>e;Kk;// mjy:现有的节点p和当前插入的键key完全相同(hash和equals方法都相同) if(p.hash==hash&&((k=p.key)==key||(key!=null&&key.equals(k))))// mjy:e赋值为p,之后会更新 e=p;elseif(pinstanceofTreeNode)// 当前节点是TreeNode(红黑树节点) e=((TreeNode<K,V>)p).putTreeVal(this,tab,hash,key,value);else{// mjy:否则就是链表,通过链表遍历处理冲突 for(intbinCount=0;;++binCount){// mjy:(e = p.next为获取当前节点的下一个节点) // mjy:条件成立即为遍历到了链表的最后一个元素,没找到key一致的node,newNode新节点加入链表末尾,跳出循环 if((e=p.next)==null){p.next=newNode(hash,key,value,null);// 链表长度超过TREEIFY_THRESHOLD - 1,树化 if(binCount>=TREEIFY_THRESHOLD-1)// -1 for 1st treeifyBin(tab,hash);break;}// mjy:e != null // mjy:条件成立即为遍历中,找到了和当前插入的键key完全相同(hash和equals方法都相同)的数据,跳出循环 if(e.hash==hash&&((k=e.key)==key||(key!=null&&key.equals(k))))break;/**
* mjy:将p移动到下个节点
* e = p.next
* e != null * p = e ==》p.next
*/p=e;}}// mjy:找到已经存在的节点,需要替换 if(e!=null){// existing mapping for key VoldValue=e.value;if(!onlyIfAbsent||oldValue==null)e.value=value;afterNodeAccess(e);returnoldValue;}}// mjy:哈希表(散列表)结构被修改的次数,替换不算 ++modCount;// mjy:插入新元素后size自增,如果自增后的值大于阈值,触发扩容 if(++size>threshold)resize();afterNodeInsertion(evict);returnnull;}
/**
* Returns the value to which the specified key is mapped,
* or {@code null} if this map contains no mapping for the key.
*
* <p>More formally, if this map contains a mapping from a key
* {@code k} to a value {@code v} such that {@code (key==null ? k==null :
* key.equals(k))}, then this method returns {@code v}; otherwise
* it returns {@code null}. (There can be at most one such mapping.)
*
* <p>A return value of {@code null} does not <i>necessarily</i>
* indicate that the map contains no mapping for the key; it's also
* possible that the map explicitly maps the key to {@code null}.
* The {@link #containsKey containsKey} operation may be used to
* distinguish these two cases.
*
* @see #put(Object, Object)
*/publicVget(Objectkey){Node<K,V>e;return(e=getNode(hash(key),key))==null?null:e.value;}/**
* Implements Map.get and related methods * * @param hash hash for key
* @param key the key
* @return the node, or null if none
*/finalNode<K,V>getNode(inthash,Objectkey){/**
* tab:当前哈希表(数组)
* first:桶位的头元素
* e:临时node元素
* n:数组长度
*/Node<K,V>[]tab;Node<K,V>first,e;intn;Kk;// mjy:当前数组不为空且长度大于0,且桶位中的头元素不为空 if((tab=table)!=null&&(n=tab.length)>0&&(first=tab[(n-1)&hash])!=null){// mjy: 头元素和要取的元素完全一致,直接返回 if(first.hash==hash&&// always check first node ((k=first.key)==key||(key!=null&&key.equals(k))))returnfirst;// mjy:不一致,判断(链表中)头元素的下个节点不为空 if((e=first.next)!=null){// mjy:红黑树 if(firstinstanceofTreeNode)return((TreeNode<K,V>)first).getTreeNode(hash,key);do{// mjy:链表,循环判断 if(e.hash==hash&&((k=e.key)==key||(key!=null&&key.equals(k))))returne;}while((e=e.next)!=null);}}returnnull;}
/**
* Removes the mapping for the specified key from this map if present.
*
* @param key key whose mapping is to be removed from the map
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/publicVremove(Objectkey){Node<K,V>e;return(e=removeNode(hash(key),key,null,false,true))==null?null:e.value;}/**
* Implements Map.remove and related methods * * @param hash hash for key
* @param key the key
* @param value the value to match if matchValue, else ignored
* @param matchValue if true only remove if value is equal
* @param movable if false do not move other nodes while removing
* @return the node, or null if none
*/finalNode<K,V>removeNode(inthash,Objectkey,Objectvalue,booleanmatchValue,booleanmovable){/**
* mjy:
* tab:当前哈希表(数组)
* p:当前node元素
* n:数组长度
* index:删除的元素数组下标
*/Node<K,V>[]tab;Node<K,V>p;intn,index;// 都不为空,有元素,需要进行查找并删除 if((tab=table)!=null&&(n=tab.length)>0&&(p=tab[index=(n-1)&hash])!=null){/**
* node:查找到的结果
* e:临时,链表的下个元素,循环查找
*/Node<K,V>node=null,e;Kk;Vv;// 当前桶位的头元素,即为要删除的元素(完全相等)赋值给node if(p.hash==hash&&((k=p.key)==key||(key!=null&&key.equals(k)))){node=p;}// mjy:头元素的下个元素不为空 elseif((e=p.next)!=null){// mjy:当前桶位是红黑树 if(pinstanceofTreeNode)node=((TreeNode<K,V>)p).getTreeNode(hash,key);else{// mjy:链表,循环查找,赋值给node do{if(e.hash==hash&&((k=e.key)==key||(key!=null&&key.equals(k)))){node=e;break;}p=e;}while((e=e.next)!=null);}}// mjy:node不为空,查找到了需要删除的元素,对比value if(node!=null&&(!matchValue||(v=node.value)==value||(value!=null&&value.equals(v)))){// 红黑树的删除逻辑 if(nodeinstanceofTreeNode)((TreeNode<K,V>)node).removeTreeNode(this,tab,movable);// mjy:桶位的头节点即为要删除的元素 elseif(node==p)// 当前桶位置指向 node的下个节点 tab[index]=node.next;else// 当前p的下个节点,指向要删除的node的下个节点(即移除了node) p.next=node.next;++modCount;--size;afterNodeRemoval(node);returnnode;}}returnnull;}