当前位置: 首页 > 后端技术 > Java

JavaHashMap源码

时间:2023-04-01 21:24:13 Java

如果不传入HashMap源属性的默认长度,则默认长度为16/***默认初始容量-MUST是2的幂。*/staticfinalintDEFAULT_INITIAL_CAPACITY=1<<4;//aka16threshold可以容纳的键值对的极限,超过这个值就需要扩容/***下一个调整大小的size值(容量*负载因子)*/整数阈值;底层数组HashMap存储最底层的Node节点数组在第一次使用时初始化,HashMap的长度始终为2的N次方,扩容后长度始终为n次方2、这是因为在计算Node的索引时,使用了(n-1)&hash操作进行优化。(&比%效率更高),相当于对n取模,即h%n/***表,在第一次使用时初始化,并根据需要调整大小。分配时,长度始终是2的幂。*(我们还容忍某些操作中的长度为零,以允许*当前不需要的引导机制。)*/transientNode[]table;如果不传入负载因子,默认为0.75负载因子太大会导致空间利用率低,太小会导致碰撞概率较高,查询效率较低/***构造函数中未指定时使用的负载因子。*/staticfinalfloatDEFAULT_LOAD_FACTOR=0.75f;展开为树Threshold当链表长度大于8时,将转化为红黑树/***使用树ra的bin计数阈值除了*bin的列表。将元素添加到至少具有这么多节点的容器时,容器将转换为树。该值必须大于*2,并且至少应为8,以符合*树木移除中关于在收缩时转换回普通垃圾箱的假设。*/staticfinalintTREEIFY_THRESHOLD=8;当树中的节点少于6个时,回到链表的阈值,它将被转换为链表/***在调整大小操作期间取消(拆分)bin的bin计数阈值。应小于TREEIFY_THRESHOLD,并且至多6以在移除下使用收缩检测进行网格划分。*/staticfinalintUNTREEIFY_THRESHOLD=6;当小于64时,表示底层元素不多,但分配到某个位置的元素较多。不是转成红黑树,而是先扩展底层数组,展开/***bins可能被树化的最小表容量。*(否则,如果bin中的节点太多,则调整表的大小。)*应至少为4*TREEIFY_THRESHOLD以避免冲突*调整大小和树化阈值之间。*/staticfinalintMIN_TREEIFY_CAPACITY=64;在treeifyBin(节点<K,V>[]tab,inthash)方法,如果当前底层数组长度小于MIN_TREEIFY_CAPACITY,会扩容if(tab==null||(n=tab.length)>>16);}putprocessfinalVputVal(inthash,Kkey,Vvalue,booleanonlyIfAbsent,booleanevict){Node[]标签;节点p;诠释n,我;//第一次使用,底层数组还没有初始化,所以初始化if((tab=table)==null||(n=tab.length)==0)n=(tab=resize())。长度;//n为底层数组的长度,(n-1)&hash结果为[0,n-1]//如果为null,则这里没有Node,新建一个Node放这里即可//HashMap保证扩容后的长度n始终是2的次方,因为&比%高效//所以采用(n-1)&hash操作进行优化,相当于对n取模,即h%nif((p=tab[i=(n-1)&hash])==null)tab[i]=newNode(hash,key,value,null);else{节点e;Kk;//判断Key是否已经存在,如果key存在,直接处理冲突if(p.hash==hash&&((k=p.key)==key||(key!=null&&key.equals(k))))e=p;//已经转化为红黑树elseif(pinstanceofTreeNode)//将节点插入红黑树e=((TreeNode)p).putTreeVal(this,tab,hash,核心价值);else{//还是一个链表,使用zipper方式处理冲突for(intbinCount=0;;++binCount){//遍历到链表的末尾,说明链表没有冲突keyinthelinkedlistif((e=p.next)==null){//在链表末尾插入一个新节点p.next=newNode(hash,key,value,null);//在链表尾部插入一个新的Node,此时链表的长度为binCount+1//等价于:链表长度(binCount+1)>=TREEIFY_THRESHOLDif(binCount>=TREEIFY_THRESHOLD-1)//-1for1st//转换为红黑树treeifyBin(tab,hash);休息;}//链表中有Key,跳出循环,处理冲突如果(e.hash==hash&&((k=e.key)==key||(key!=null&&key.equals(k))))break;p=e;}}//处理键冲突If(e!=null){//键的现有映射VoldValue=e.value;if(!onlyIfAbsent||oldValue==null)//将冲突节点的值替换为e.value=value;节点访问后(e);//返回被替换节点的值returnoldValue;}}++模数;//Expandif(++size>threshold)if(++size>threshold)超过可以容纳KV对的阈值。afterNodeInsertion(逐出);returnnull;}扩展方法finalNode[]resize(){Node[]oldTab=table;intoldCap=(oldTab==null)?0:旧标签长度;intoldThr=阈值;intnewCap,newThr=0;if(oldCap>0){//如果超过最大值,则不进行扩容,此时会发生大量碰撞if(oldCap>=MAXIMUM_CAPACITY){threshold=Integer.MAX_VALUE;返回旧标签;}//扩展为原始的两倍elseif((newCap=oldCap<<1)=DEFAULT_INITIAL_CAPACITY)newThr=oldThr<<1;//双阈值}elseif(oldThr>0)//初始容量被置于阈值newCap=oldThr;else{//零初始阈值表示使用默认值newCap=DEFAULT_INITIAL_CAPACITY;newThr=(int)(DEFAULT_LOAD_FACTOR*DEFAULT_INITIAL_CAPACITY);}//计算新的KV对付值if(newThr==0){floatft=(float)newCap*loadFactor;newThr=(newCap[]newTab=(Node[])newNode[newCap];表=新标签;if(oldTab!=null){//重新计算索引并将其放入新表中for(intj=0;je;if((e=oldTab[j])!=null){oldTab[j]=null;如果(e.next==null)newTab[e.hash&(newCap-1)]=e;elseif(einstanceofTreeNode)((TreeNode)e).split(this,newTab,j,oldCap);else{//保留顺序NodeloHead=null,loTail=null;NodehiHead=null,hiTail=null;下一个节点;做{next=e.next;if((e.hash&oldCap)==0){if(loTail==null)loHead=e;否则loTail.next=e;尾巴=e;}else{if(hiTail==null)hiHead=e;否则hiTail.next=e;高尾=e;}}while((e=next)!=null);if(loTail!=null){loTail.next=null;newTab[j]=loHead;}if(hiTail!=null){hiTail.next=null;newTab[j+oldCap]=hiHead;}}}}}返回新标签;}