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

HashMap自动扩容机制源码详解

时间:2023-04-02 00:18:17 Java

一、简介HashMap的源码前面已经讲解过了。一个数组添加到一个链表中,当链表过长时,分裂成红黑树。自动扩容机制没有细说。今天让我们仔细看看。2、扩容机制会从结论开始:hashmap的容量是2的倍数,比如2、4、8、16、32、64……每次扩容,就扩容1倍、2比4、4比8、8比16、16比32等。扩展系数:默认0.75,也可以指定小数。扩容时间点:当容器中的元素个数达到:容量*扩容系数三时开始扩容,源码分析(1)先看构造函数staticfinalintDEFAULT_INITIAL_CAPACITY=1<<4;//又名16staticfinalfloatDEFAULT_LOAD_FACTOR=0.75f;publicHashMap(){this.loadFactor=DEFAULT_LOAD_FACTOR;//其他字段默认}default构造函数指定扩展系数:0.75,默认容量为16publicHashMap(intinitialCapacity){this(initialCapacity,DEFAULT_LOAD_FACTOR);}指定初始容量,默认扩展系数:0.75publicHashMap(intinitialCapacity,floatloadFactor){if(initialCapacity<0)thrownewIllegalArgumentException("非法初始容量:"+initialCapacity);如果(initialCapacity>MAXIMUM_CAPACITY)initialCapacity=MAXIMUM_CAPACITY;如果(loadFactor<=0||Float.isNaN(loadFactor))thrownewIllegalArgumentException(":"+负载因子);this.loadFactor=loadFactor;this.threshold=tableSizeFor(initialCapacity);}同时指定初始容量和扩展因子/***调整大小的下一个大小值(容量*负载因子)。**@serial*/int阈值;注意这个变量:下一个要扩容的值,容量扩容,容量*扩容因子看到这句话:this.threshold=tableSizeFor(initialCapacity);/***Returnsapoweroftwosizeforthegiventargetcapacity*/staticfinalinttableSizeFor(intcap){intn=cap-1;n|=n>>>1;n|=n>>>2;n|=n>>>4;n|=n>>>8;n|=n>>>16;返回(n<0)?1:(n>=MAXIMUM_CAPACITY)?MAXIMUM_CAPACITY:n+1;}这个方法是给定值四舍五入后是2的倍数,比如3--->4,15->16,27->32至此准备工作就做好了,我们来看put方法(2)put方法publicVput(Kkey,Vvalue){returnputVal(hash(key),key,value,false,true);}finalVputVal(inthash,Kkey,Vvalue,booleanonlyIfAbsent,布尔逐出){Node[]tAB;节点p;诠释n,我;//①最开始table为null,调用resize()方法if((tab=table)==null||(n=tab.length)==0)n=(tab=resize()).length;如果((p=tab[i=(n-1)&hash])==null)tab[i]=newNode(hash,key,value,null);else{节点e;Kk;if(p.hash==hash&&((k=p.key)==key||(key!=null&&key.equals(k))))e=p;elseif(pinstanceofTreeNode)e=((TreeNode)p).putTreeVal(this,tab,hash,key,value);else{for(intbinCount=0;;++binCount){if((e=p.next)==null){p.next=newNode(hash,key,value,null);if(binCount>=TREEIFY_THRESHOLD-1)//-1对于第一个treeifyBin(tab,hash);休息;}if(e.hash==hash&&((k=e.key)==键||(key!=null&&key.equals(k))))中断;p=e;}}if(e!=null){//键VoldValue=e.value的现有映射;如果(!onlyIfAbsent||oldValue==null)e.value=value;节点访问后(e);返回旧值;}}++模数;//②最后判断容量是否大于扩展容量,调用resize方法if(++size>threshold)resize();节点插入后(逐出);returnnull;}①表一开始为null,调用resize()方法②最后判断容量是否大于扩容容量,如果大于则调用resize()方法见resize()方法finalNode[]resize(){Node[]oldTab=table;intoldCap=(oldTab==null)?0:旧标签长度;intoldThr=阈值;intnewCap,newThr=0;if(oldCap>0){if(oldCap>=MAXIMUM_CAPACITY){threshold=Integer.MAX_VALUE;返回旧标签;}否则如果((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);}if(newThr==0){floatft=(float)newCap*loadFactor;newThr=(newCap[]newTab=(Node[])newNode[newCap];表=新标签;if(oldTab!=null){for(intj=0;je;如果((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;loTail=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;}}}}}returnnewTab;}先分析第一种情况:Mapmap=newHashMap();取最后一个分支,capacity为16,扩容容量为12else{newCap=DEFAULT_INITIAL_CAPACITY;newThr=(int)(DEFAULT_LOAD_FACTOR*DEFAULT_INITIAL_CAPACITY);}分析第二种情况:Mapmap=newHashMap(20);取第二个分支,之前分析过,threshold=tableSizeFor(20)是32newcapacity=oldThris32//capacityelseif(oldThr>0)//初始容量放在thresholdnewCap=oldThr;新扩容newThr=newCap*loadFactoris24//扩容if(newThr==0){floatft=(float)newCap*loadFactor;newThr=(newCap0){if(oldCap>=MAXIMUM_CAPACITY){threshold=Integer.MAX_VALUE;返回旧标签;}elseif((newCap=oldCap<<1)=DEFAULT_INITIAL_CAPACITY)newThr=oldThr<<1;//双阈值}最后将元素复制到新表中,直接复制单个元素。如果是树,调用树的copy方法。如果是链表,循环链表复制欢迎关注微信公众号:风记,更多技术学习分享