专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

HashMap底层实现原理

  HashMap采用NodeK,V数组来存储keyvalue对,每一个键值对组成了一个Node实体,Node类实际上是一个单向的链表结构,它具有Next指针,可以连接下一个Node实体。HashMap在JDK1。8之前和之后的区别
  JDK1。8之前,数组链表存储结构
  缺点就是哈希函数很难使元素百分百的均匀分布,这会产生一种极端的可能,就是大量的元素存在一个桶里
  JDK1。8之后:数组链表红黑树添加元素时:链表长度大于8的时候,转换为红黑树删除元素、扩容时,同上,数量大于8时,也是采用红黑树形式存储,但是在数量较少时,即数量小于6时,会将红黑树转换回链表遍历、查找时,使用红黑树,他的时间复杂度O(logn),便于性能的提高。数据结构
  存储结构
  staticclassNodeK,VimplementsMap。EntryK,V{finalinthash;finalKkey;Vvalue;NodeK,Vnext;Node(inthash,Kkey,Vvalue,NodeK,Vnext){this。hashhash;this。keykey;this。valuevalue;this。nextnext;}publicfinalKgetKey(){returnkey;}publicfinalVgetValue(){returnvalue;}publicfinalStringtoString(){returnkeyvalue;}publicfinalinthashCode(){returnObjects。hashCode(key)Objects。hashCode(value);}publicfinalVsetValue(VnewValue){VoldValuevalue;valuenewValue;returnoldValue;}publicfinalbooleanequals(Objecto){if(othis)returntrue;if(oinstanceofMap。Entry){Map。Entrylt;?,?e(Map。Entrylt;?,?)o;if(Objects。equals(key,e。getKey())Objects。equals(value,e。getValue()))returntrue;}returnfalse;}}
  NodeK,V数组transientNodeK,V〔〕table;加载因子finalfloatloadFactor;默认加载因子,容量达到这个比例时自动扩容staticfinalfloatDEFAULTLOADFACTOR0。75f;数量大于8时,链表转换为红黑树形式存储staticfinalintTREEIFYTHRESHOLD8;即数量小于6时,会将红黑树转换回链表,删除元素时removestaticfinalintUNTREEIFYTHRESHOLD6;PUT时每次都做hashpublicVput(Kkey,Vvalue){returnputVal(hash(key),key,value,false,true);}put函数核心算法finalVputVal(inthash,Kkey,Vvalue,booleanonlyIfAbsent,booleanevict){NodeK,V〔〕tab;NodeK,Vp;intn,i;if((tabtable)null(ntab。length)0)n(tabresize())。length;if((ptab〔i(n1)hash〕)null)这里的n表示数组的长度。hashtab〔i〕newNode(hash,key,value,null);else{NodeK,Ve;Kk;if(p。hashhash((kp。key)key(key!nullkey。equals(k))))ep;elseif(pinstanceofTreeNode)e((TreeNodeK,V)p)。putTreeVal(this,tab,hash,key,value);else{for(intbinCount0;;binCount){if((ep。next)null){p。nextnewNode(hash,key,value,null);if(binCountTREEIFYTHRESHOLD1)1for1sttreeifyBin(tab,hash);break;}if(e。hashhash((ke。key)key(key!nullkey。equals(k))))break;pe;}}if(e!null){existingmappingforkeyVoldValuee。value;if(!onlyIfAbsentoldValuenull)e。valuevalue;afterNodeAccess(e);空实现returnoldValue;}}modCount;modCount是java集合中FailFast的底层实现原理if(sizethreshold)扩容resize();afterNodeInsertion(evict);空实现returnnull;}CallbackstoallowLinkedHashMappostactionsvoidafterNodeAccess(NodeK,Vp){}voidafterNodeInsertion(booleanevict){}voidafterNodeRemoval(NodeK,Vp){}思考
  java集合中的快速失败:modCount
  快速失败是Java集合的一种错误检测机制,当多个线程对集合进行结构上的改变的操作时,有可能会产生failfast。
  举个例子:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就可能会抛出ConcurrentModificationException异常,从而产生fastfail快速失败。
  HashMap中遍历算法如下:finalclassKeySetextendsAbstractSetK{publicfinalintsize(){returnsize;}publicfinalvoidclear(){HashMap。this。clear();}publicfinalIteratorKiterator(){returnnewKeyIterator();}publicfinalbooleancontains(Objecto){returncontainsKey(o);}publicfinalbooleanremove(Objectkey){returnremoveNode(hash(key),key,null,false,true)!null;}publicfinalSpliteratorKspliterator(){returnnewKeySpliterator(HashMap。this,0,1,0,0);}publicfinalvoidforEach(Consumerlt;?superKaction){NodeK,V〔〕tab;if(actionnull)thrownewNullPointerException();if(size0(tabtable)!null){intmcmodCount;for(inti0;itab。length;i){for(NodeK,Vetab〔i〕;e!null;ee。next)action。accept(e。key);}if(modCount!mc)抛出异常,FailFastthrownewConcurrentModificationException();}}}优化hash算法
  JDK1。8中,是通过hashCode()的高16位异或低16位实现的:(hk。hashCode())(h16),主要是从速度,功效和质量来考虑的,减少系统的开销,也不会造成因为高位没有参与下标的计算,从而引起的碰撞。扰动函数:促使元素位置分布均匀,减少碰撞几率staticfinalinthash(Objectkey){inth;如果keynull返回的值是0如果key!null首先计算key的hashcode值赋值给h,然后与h无符号右移16位后的二进制按位异或预算得到最后hash值return(keynull)?0:(hkey。hashCode())(h16);}hash具体实现过程如下图所示:
  hash计算过程与putval函数的应用key。hashCode();返回散列值也就是hashcode,假设随便生成的一个值。n表示数组初始化的长度是16。(按位与运算):运算规则:相同的二进制数位上,都是1的时候,结果为1,否则为零。(按位异或运算):运算规则:相同的二进制数位上,数字相同,结果为0,不同为1。
  高16bit不变,低16bit和高16bit做了一个异或(得到的hashCode转化为32位二进制,前16位和后16位低16bit和高16bit做了一个异或)为什么这样实现呢?
  如果当n即数组长度很小,假设是16的话,那么n1即为1111,这样的值和hashCode直接做按位与操作,实际上只使用了哈希值的后4位。如果当哈希值的高位变化很大,低位变化很小,这样就很容易造成哈希冲突了,所以这里把高低位都利用起来,从而解决了这个问题。降低了Hash冲突的概率为什么要用异或运算符
  保证了对象的hashCode的32位值只要有一位发生改变,整个hash()返回值就会改变。尽可能的减少碰撞。
  工作原理
  存储对象时,将KV键值传给put()方法:调用hash(K)方法计算K的hash值,然后结合数组长度,计算得数组下标;调整数组大小(当容器中的元素个数大于capacityloadfactor时,容器会进行扩容resize为2n);hash碰撞
  i。如果K的hash值在HashMap中不存在,则执行插入,若存在,则发生碰撞;
  ii。如果K的hash值在HashMap中存在,且它们两者equals返回true,则更新键值对;
  iii。如果K的hash值在HashMap中存在,且它们两者equals返回false,则插入链表的尾部(尾插法)或者红黑树中(树的添加方式)。(JDK1。7之前使用头插法、JDK1。8使用尾插法)
  get实现publicVget(Objectkey){NodeK,Ve;return(egetNode(hash(key),key))null?null:e。value;}ImplementsMap。getandrelatedmethodsparamhashhashforkeyparamkeythekeyreturnthenode,ornullifnonefinalNodeK,VgetNode(inthash,Objectkey){NodeK,V〔〕tab;NodeK,Vfirst,e;intn;Kk;if((tabtable)!null(ntab。length)0(firsttab〔(n1)hash〕)!null){if(first。hashhashalwayscheckfirstnode((kfirst。key)key(key!nullkey。equals(k))))returnfirst;if((efirst。next)!null){if(firstinstanceofTreeNode)return((TreeNodeK,V)first)。getTreeNode(hash,key);do{if(e。hashhash((ke。key)key(key!nullkey。equals(k))))returne;}while((ee。next)!null);}}returnnull;}
  问题思考?
  Q:默认初始化大小是多少?为啥是这么多?为啥大小都是2的幂?
  A:hash运算的过程其实就是对目标元素的Key进行hashcode,再对Map的容量进行取模,而JDK的工程师为了提升取模的效率,使用位运算代替了取模运算,这就要求Map的容量一定得是2的幂。HashMap的容量为什么是2的n次幂,和这个putval方法中(n1)hash的计算方法有着千丝万缕的关系,符号是按位与的计算,这是位运算,计算机能直接运算,特别高效,按位与的计算方法是,只有当对应位置的数据都为1时,运算结果也为1,当HashMap的容量是2的n次幂时,(n1)的2进制也就是1111111111这样形式的,这样与添加元素的hash值进行位运算时,能够(充分的散列),使得添加的元素均匀分布在HashMap的每个位置上,减少hash碰撞。
  Q:HashMap如何有效减少碰撞?
  A:扰动函数:促使元素位置分布均匀,减少碰撞几率、使用final对象,并采用合适的equals()和hashCode()方法

cba积分榜截止到1月2日昨日cba共进行了四场比赛广东东莞大益战胜同曦,继续排名第二辽宁队经过加时,4分险胜吉林,赢得东北德比天津8991惜败北京首钢浙江11886大胜四川本轮结束过后浙江,广东,辽宁,依CBA最新排名出炉!前3竞争白热化,3队争第4,北控跌入谷底北京时间1月3日,CBA常规赛第21轮继续开打,对于积分榜的格局来说,随着浙江,广东,辽宁继续获胜,前四的格局进入到白热化的状态,上海,深圳,北京处于你追我赶的状态,这三支球队争夺詹金斯亚当斯的篮板太强了,我为球员们骄傲今天灰熊以118108战胜国王。赛后,灰熊主帅泰勒詹金斯接受了记者的采访。本场比赛,灰熊球员史蒂文亚当斯出战34分钟,12投5中,罚球2投1中,得到11分23篮板3助攻1抢断2封盖步哈登后尘?独行侠全靠一人支撑,数据爆炸夺冠希望却愈发渺茫!在这个数据井喷的时代,砍下高得分或者三双已经不让人意外。但作为联盟新生代的翘楚,卢卡东契奇正在不断刷新外界认知,比如他曾说在NBA得分比欧洲联赛更容易,事实证明卢卡东契奇并非吹牛,国王杯3险胜西协甲球队法蒂第103分钟进球制胜北京时间1月5日凌晨4点,202223赛季西班牙国王杯116决赛,巴塞罗那客场对阵西班牙协会联赛甲组球队城际CF。上半场,阿劳霍的进球帮助巴萨取得领先,下半场两队交替进球,主队拉玛英超0水晶宫北京时间1月5日凌晨4点,202223赛季英超联赛第19轮,托特纳姆热刺客场挑战水晶宫。上半场,两队都没有进球下半场,迎来英超三百场里程碑的凯恩在5分钟内梅开二度并送出助攻,多尔蒂意甲0送那不勒斯首败哲科头球制胜北京时间1月5日凌晨3点45分,202223赛季意甲联赛第16轮迎来一场焦点战,国际米兰坐镇主场迎战联赛领头羊那不勒斯。上半场,两队都没有进球下半场,迪马尔科助攻哲科破门。最终,国意甲0克雷莫内塞七连胜稳居前三北京时间1月5日凌晨1点30分,202223赛季意甲联赛第16轮,尤文图斯客场对阵克雷莫内塞。上半场,德瑟斯进球因犯规被判无效下半场,德瑟斯的射门击中立柱,补时阶段米利克任意球绝杀Woj锡安不是轻微拉伤,至少缺阵一个月以上鹈鹕当家球星锡安威廉姆森在1月3日与76人之战的第三节遭遇腿筋伤势提前退出比赛,昨天TheAthletic记者ShamsCharania更新伤情,说他至少缺阵三周。ESPN记者Wo六岁前的感统基础,如何影响长大后的高阶能力?我们经常说感觉统合能力是一切能力的基础,基础不足,后面的高阶能力是发展不起来的。下面我们通过一位朋友提出的问题来解说六岁前的感统基础,到底如何影响长大后的高阶能力?01hr基础不足深圳官宣王建军因身体原因无法继续带队郑永刚正式接过球队教鞭北京时间1月5日,今日CBA球队深圳队通过官方社媒平台发布公告宣布主教练王建军指导因心脏射频消融术后出现病情反复,需要长时间休养,无法继续带队,俱乐部决定对教练团队进行调整,代理主
尼克松问毛主席您有什么特长?主席回答后,尼克松向他尊敬鞠躬尼克松问毛主席您有什么特长?主席回答后,尼克松向他尊敬鞠躬1976年,尼克松第二次访问中国,不同于前一次的是,此时的尼克松已经卸任美国总统一职,但毛主席仍亲自接待尼克松。在二者交谈蒋孝勇为完成祖父遗愿,提出两蒋移灵大陆,公开支持一个中国前言1975年4月5日,蒋介石因心脏病在台北士林寓所去世,但始终没有下葬,他的灵柩被暂时安放在桃园县大溪镇的慈湖陵寝,静静等待着归葬故土的时机。蒋介石出生于浙江奉化,根据当地习俗,74年,李讷离婚后带儿子回中南海,毛主席落泪有困难就和爸爸说1973年12月26日,毛主席迎来了80岁诞辰,他借着生日机会,将儿女召集到自己身边,唯独主席最疼爱的小女儿李讷没在自己身边。主席担心自己身体不行,感慨有生之年能否再看到李讷。19清朝亡了近百年,为啥如今还有人守皇陵,他们是谁,工资谁来发?清朝是我国最后存在的封建王朝,它除了留下数百年的统治历史之外,还留下了许多规模宏大的皇陵。它们虽然被盗了许多次,墓中已经没有什么陪葬品,但其建筑本身就是一种文物,同样值得好好珍惜。清朝末代格格改名换姓活到2014年,临终前说出来一件皇室丑闻她的奇不仅仅是她的身份和身世,而是在于她那令人称奇的心态和性格。80多岁的老人历经一生磨难仍保持豁达开朗上文中提到的她名为金默玉,她还有另一个名字叫做爱新觉罗显琦,看到爱新觉罗这四毛主席晚年为堂弟破例,写信嘱咐政府替我多照顾他一点70年代,湖南韶山政府忽然接到毛主席写来的一封亲笔信。信中语气诚恳地对当地政府说我老了,请政府替我多照顾他一点吧!毛主席说的这个需要政府照顾的人,正是他的堂弟毛泽连。这倒怪了,众所志愿军班长半夜下山时,忽然发觉情况不对那边的小树怎么动了?从1951年下半年开始,志愿军在三八线附近与敌人形成了对峙局面,抗美援朝战争由此进入了阵地战阶段。从阵地战开始,志愿军在三八线附近修筑了大量防御工事,阻挡敌人的进攻,先后粉碎了敌人2009年,毛新宇携妻到浙江衢州一山村寻祖,村民几乎都是毛姓人家2009年5月22日上午,浙江省江山市石门镇清漾村迎来了两位贵宾。毛新宇携妻子刘滨走进村时,早已守候在村口的村民们敲锣打鼓,欢呼雀跃,数千村民出户相迎。无他,这是对远归族人的善意与古代流放女囚来回数千里,为何衙役们争着押送?头条创作挑战赛我们经常在电视剧里看到,有些犯了重罪的人会被流放,听起来好像流放就是被放逐到其他的地方,觉得也没什么。但实际上却并非如此流放的这些罪犯需要在衙役的押运下,带着重重的镣1965年李宗仁回国,毛泽东却未给他一官半职,这是为何?说到李宗仁,他和白崇禧一样,曾经都是桂系军阀的核心人物,论当时的影响力,那绝对没话说的。白崇禧李宗仁著名的台儿庄战役,就是李宗仁亲自指挥打的。1965年,李宗仁突破美国的重重封锁,港媒中国快马加鞭推动新能源发展参考消息网9月21日报道香港南华早报网站9月19日刊登题为中国正快马加鞭推动新能源发展,全文编译如下在中国南方山区的一个规模不大的县里,王荣硕的公司在一项为当地屋顶安装光伏发电板的
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网