纠纷奇闻社交美文家庭
投稿投诉
家庭城市
爱好生活
创业男女
能力餐饮
美文职业
心理周易
母婴奇趣
两性技能
社交传统
新闻范文
工作个人
思考社会
作文职场
家居中考
兴趣安全
解密魅力
奇闻笑话
写作笔记
阅读企业
饮食时事
纠纷案例
初中历史
说说童话
乐趣治疗

HashMap夺命14问,你能坚持到第几问?

8月4日 碧落盟投稿
  1。HashMap的底层数据结构是什么?
  在JDK1。7中和JDK1。8中有所区别:
  在JDK1。7中,由数组链表组成,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的。
  在JDK1。8中,有数组链表红黑树组成。当链表过长,则会严重影响HashMap的性能,红黑树搜索时间复杂度是O(logn),而链表是O(n)。因此,JDK1。8对数据结构做了进一步的优化,引入了红黑树,链表和红黑树在达到一定条件会进行转换:当链表超过8且数组长度(数据总量)超过64才会转为红黑树将链表转换成红黑树前会判断,如果当前数组的长度小于64,那么会选择先进行数组扩容,而不是转换为红黑树,以减少搜索时间。
  2。说一下HashMap的特点hashmap存取是无序的键和值位置都可以是null,但是键位置只能是一个null键位置是唯一的,底层的数据结构是控制键的jdk1。8前数据结构是:链表数组jdk1。8之后是:数组链表红黑树阈值(边界值)8并且数组长度大于64,才将链表转换成红黑树,变成红黑树的目的是提高搜索速度,高效查询3。解决hash冲突的办法有哪些?HashMap用的哪种?
  解决Hash冲突方法有:开放定址法、再哈希法、链地址法(HashMap中常见的拉链法)、简历公共溢出区。HashMap中采用的是链地址法。开放定址法也称为再散列法,基本思想就是,如果pH(key)出现冲突时,则以p为基础,再次hash,p1H(p),如果p1再次出现冲突,则以p1为基础,以此类推,直到找到一个不冲突的哈希地址pi。因此开放定址法所需要的hash表的长度要大于等于所需要存放的元素,而且因为存在再次hash,所以只能在删除的节点上做标记,而不能真正删除节点再哈希法(双重散列,多重散列),提供多个不同的hash函数,R1H1(key1)发生冲突时,再计算R2H2(key1),直到没有冲突为止。这样做虽然不易产生堆集,但增加了计算的时间。链地址法(拉链法),将哈希值相同的元素构成一个同义词的单链表,并将单链表的头指针存放在哈希表的第i个单元中,查找、插入和删除主要在同义词链表中进行,链表法适用于经常进行插入和删除的情况。建立公共溢出区,将哈希表分为公共表和溢出表,当溢出发生时,将所有溢出数据统一放到溢出区
  注意开放定址法和再哈希法的区别是开放定址法只能使用同一种hash函数进行再次hash,再哈希法可以调用多种不同的hash函数进行再次hash4。为什么要在数组长度大于64之后,链表才会进化为红黑树
  在数组比较小时如果出现红黑树结构,反而会降低效率,而红黑树需要进行左旋右旋,变色,这些操作来保持平衡,同时数组长度小于64时,搜索时间相对要快些,总之是为了加快搜索速度,提高性能
  JDK1。8以前HashMap的实现是数组链表,即使哈希函数取得再好,也很难达到元素百分百均匀分布。当HashMap中有大量的元素都存放在同一个桶中时,这个桶下有一条长长的链表,此时HashMap就相当于单链表,假如单链表有n个元素,遍历的时间复杂度就从O(1)退化成O(n),完全失去了它的优势,为了解决此种情况,JDK1。8中引入了红黑树(查找的时间复杂度为O(logn))来优化这种问题5。为什么加载因子设置为0。75,初始化临界值是12?
  HashMap中的threshold是HashMap所能容纳键值对的最大值。计算公式为lengthLoadFactory。也就是说,在数组定义好长度之后,负载因子越大,所能容纳的键值对个数也越大
  loadFactory越趋近于1,那么数组中存放的数据(entry也就越来越多),数据也就越密集,也就会有更多的链表长度处于更长的数值,我们的查询效率就会越低,当我们添加数据,产生hash冲突的概率也会更高
  默认的loadFactory是0。75,loadFactory越小,越趋近于0,数组中个存放的数据(entry)也就越少,表现得更加稀疏
  0。75是对空间和时间效率的一种平衡选择
  如果负载因子小一些比如是0。4,那么初始长度160。46,数组占满6个空间就进行扩容,很多空间可能元素很少甚至没有元素,会造成大量的空间被浪费
  如果负载因子大一些比如是0。9,这样会导致扩容之前查找元素的效率非常低
  loadfactory设置为0。75是经过多重计算检验得到的可靠值,可以最大程度的减少rehash的次数,避免过多的性能消耗6。哈希表底层采用何种算法计算hash值?还有哪些算法可以计算出hash值?
  hashCode方法是Object中的方法,所有的类都可以对其进行使用,首先底层通过调用hashCode方法生成初始hash值h1,然后将h1无符号右移16位得到h2,之后将h1与h2进行按位异或()运算得到最终hash值h3,之后将h3与(length1)进行按位与()运算得到hash表索引
  其他可以计算出hash值的算法有平方取中法取余数伪随机数法7。当两个对象的hashCode相等时会怎样
  hashCode相等产生hash碰撞,hashCode相等会调用equals方法比较内容是否相等,内容如果相等则会进行覆盖,内容如果不等则会连接到链表后方,链表长度超过8且数组长度超过64,会转变成红黑树节点8。何时发生哈希碰撞和什么是哈希碰撞,如何解决哈希碰撞?
  只要两个元素的key计算的hash码值相同就会发生hash碰撞,jdk8之前使用链表解决哈希碰撞,jdk8之后使用链表红黑树解决哈希碰撞9。HashMap的put方法流程
  以jdk8为例,简要流程如下:首先根据key的值计算hash值,找到该元素在数组中存储的下标如果数组是空的,则调用resize进行初始化;如果没有哈希冲突直接放在对应的数组下标里如果冲突了,且key已经存在,就覆盖掉value如果冲突后是链表结构,就判断该链表是否大于8,如果大于8并且数组容量小于64,就进行扩容;如果链表节点数量大于8并且数组的容量大于64,则将这个结构转换成红黑树;否则,链表插入键值对,若key存在,就覆盖掉value如果冲突后,发现该节点是红黑树,就将这个节点挂在树上
  10。HashMap的扩容方式
  HashMap在容量超过负载因子所定义的容量之后,就会扩容。java里的数组是无法自己扩容的,将HashMap的大小扩大为原来数组的两倍
  我们来看jdk1。8扩容的源码finalNodeK,V〔〕resize(){oldTab:引用扩容前的哈希表NodeK,V〔〕oldToldCap:表示扩容前的table数组的长度intoldCap(oldTabnull)?0:oldTab。获得旧哈希表的扩容阈值intoldTnewCap:扩容之后table数组大小newThr:扩容之后下次触发扩容的条件intnewCap,newThr0;条件成立说明hashMap中的散列表已经初始化过了,是一次正常扩容if(oldCap0){判断旧的容量是否大于等于最大容量,如果是,则无法扩容,并且设置扩容条件为int最大值,这种情况属于非常少数的情况if(oldCapMAXIMUMCAPACITY){thresholdInteger。MAXVALUE;returnoldT}设置newCap新容量为oldCap旧容量的二倍(1),并且最大容量,而且16,则新阈值等于旧阈值的两倍elseif((newCapoldCap1)MAXIMUMCAPACITYoldCapDEFAULTINITIALCAPACITY)newThroldThr1;doublethreshold}如果oldCap0并且边界值大于0,说明散列表是null,但此时oldThr0说明此时hashMap的创建是通过指定的构造方法创建的,新容量直接等于阈值1。newHashMap(intitCap,loadFactor)2。newHashMap(initCap)3。newHashMap(map)elseif(oldThr0)initialcapacitywasplacedinthresholdnewCapoldT这种情况下oldThr0;oldCap0,说明没经过初始化,创建hashMap的时候是通过newHashMap()的方式创建的else{zeroinitialthresholdsignifiesusingdefaultsnewCapDEFAULTINITIALCAPACITY;newThr(int)(DEFAULTLOADFACTORDEFAULTINITIALCAPACITY);}newThr为0时,通过newCap和loadFactor计算出一个newThrif(newThr0){容量0。75floatft(float)newCaploadFnewThr(newCapMAXIMUMCAPACITYft(float)MAXIMUMCAPACITY?(int)ft:Integer。MAXVALUE);}thresholdnewTSuppressWarnings({rawtypes,unchecked})根据上面计算出的结果创建一个更长更大的数组NodeK,V〔〕newTab(NodeK,V〔〕)newNode〔newCap〕;将table指向新创建的数组tablenewT本次扩容之前table不为nullif(oldTab!null){对数组中的元素进行遍历for(intj0;joldCj){设置e为当前node节点NodeK,Ve;当前桶位数据不为空,但不能知道里面是单个元素,还是链表或红黑树,eoldTab〔j〕,先用e记录下当前元素if((eoldTab〔j〕)!null){将老数组j桶位置为空,方便回收oldTab〔j〕如果e节点不存在下一个节点,说明e是单个元素,则直接放置在新数组的桶位if(e。nextnull)newTab〔e。hash(newCap1)〕e;如果e是树节点,证明该节点处于红黑树中elseif(einstanceofTreeNode)((TreeNodeK,V)e)。split(this,newTab,j,oldCap);e为链表节点,则对链表进行遍历else{preserveorder低位链表:存放在扩容之后的数组的下标位置,与当前数组下标位置一致loHead:低位链表头节点loTail低位链表尾节点NodeK,VloHeadnull,loT高位链表,存放扩容之后的数组的下标位置,原索引扩容之前数组容量hiHead:高位链表头节点hiTail:高位链表尾节点NodeK,VhiHeadnull,hiTNodeK,Vdo{nexte。oldCap为16:10000,与e。hsah做运算可以得到高位为1还是0高位为0,放在低位链表if((e。hasholdCap)0){if(loTailnull)loHead指向eloHelseloTail。loT}高位为1,放在高位链表else{if(hiTailnull)hiHelsehiTail。hiT}}while((enext)!null);低位链表已成,将头节点loHead指向在原位if(loTail!null){loTail。newTab〔j〕loH}高位链表已成,将头节点指向新索引if(hiTail!null){hiTail。newTab〔joldCap〕hiH}}}}}returnnewT}
  扩容之后原位置的节点只有两种调整保持原位置不动(新bit位为0时)散列原索引扩容大小的位置去(新bit位为1时)
  扩容之后元素的散列设置的非常巧妙,节省了计算hash值的时间,我们来看一下具体的实现
  当数组长度从16到32,其实只是多了一个bit位的运算,我们只需要在意那个多出来的bit为是0还是1,是0的话索引不变,是1的话索引变为当前索引值扩容的长度,比如5变成51621
  这样的扩容方式不仅节省了重新计算hash的时间,而且保证了当前桶中的元素总数一定小于等于原来桶中的元素数量,避免了更严重的hash冲突,均匀的把之前冲突的节点分散到新的桶中去11。一般用什么作为HashMap的key?
  一般用Integer、String这种不可变类当HashMap当key因为String是不可变的,当创建字符串时,它的hashcode被缓存下来,不需要再次计算,相对于其他对象更快因为获取对象的时候要用到equals()和hashCode()方法,那么键对象正确的重写这两个方法是非常重要的,这些类很规范的重写了hashCode()以及equals()方法12。为什么Map桶中节点个数超过8才转为红黑树?
  8作为阈值作为HashMap的成员变量,在源码的注释中并没有说明阈值为什么是8
  在HashMap中有这样一段注释说明,我们继续看BecauseTreeNodesareabouttwicethesizeofregularnodes,weusethemonlywhenbinscontainenoughnodestowarrantuse(seeTREEIFYTHRESHOLD)。Andwhentheybecometoosmall(duetoremovalorresizing)theyareconvertedbacktoplainbins。InusageswithwelldistributeduserhashCodes,treebinsarerarelyused。Ideally,underrandomhashCodes,thefrequencyofnodesinbinsfollowsaPoissondistribution(http:en。wikipedia。orgwikiPoissondistribution)withaparameterofabout0。5onaverageforthedefaultresizingthresholdof0。75,althoughwithalargevariancebecauseofresizinggranularity。Ignoringvariance,theexpectedoccurrencesoflistsizekare(exp(0。5)pow(0。5,k)factorial(k))。
  翻译因为树节点的大小大约是普通节点的两倍,所以我们只在箱子包含足够的节点时才使用树节点(参见TREEIFYTHRESHOLD)。当他们边的太小(由于删除或调整大小)时,就会被转换回普通的桶,在使用分布良好的hashcode时,很少使用树箱。理想情况下,在随机哈希码下,箱子中节点的频率服从泊松分布第一个值是:0:0。606530661:0。303265332:0。075816333:0。012636064:0。001579525:0。000157956:0。000013167:0。000000948:0。00000006more:lessthan1intenmillion
  树节点占用空间是普通Node的两倍,如果链表节点不够多却转换成红黑树,无疑会耗费大量的空间资源,并且在随机hash算法下的所有bin节点分布频率遵从泊松分布,链表长度达到8的概率只有0。00000006,几乎是不可能事件,所以8的计算是经过重重科学考量的从平均查找长度来看,红黑树的平均查找长度是logn,如果长度为8,则logn3,而链表的平均查找长度为n4,长度为8时,n24,所以阈值8能大大提高搜索速度当长度为6时红黑树退化为链表是因为lognlog6约等于2。6,而n2623,两者相差不大,而红黑树节点占用更多的内存空间,所以此时转换最为友好13。HashMap为什么线程不安全?多线程下扩容死循环。JDK1。7中的HashMap使用头插法插入元素,在多线程的环境下,扩容的时候有可能导致环形链表的出现,形成死循环。因此JDK1。8使用尾插法插入元素,在扩容时会保持链表元素原本的顺序,不会出现环形链表的问题多线程的put可能导致元素的丢失。多线程同时执行put操作,如果计算出来的索引位置是相同的,那会造成前一个key被后一个key覆盖,从而导致元素的丢失。此问题在JDK1。7和JDK1。8中都存在put和get并发时,可能导致get为null。线程1执行put时,因为元素个数超出threshold而导致rehash,线程2此时执行get,有可能导致这个问题,此问题在JDK1。7和JDK1。8中都存在14。计算hash值时为什么要让低16bit和高16bit进行异或处理我们计算索引需要将hashCode值与length1进行按位与运算,如果数组长度很小,比如16,这样的值和hashCode做异或实际上只有hashCode值的后4位在进行运算,hash值是一个随机值,而如果产生的hashCode值高位变化很大,而低位变化很小,那么有很大概率造成哈希冲突,所以我们为了使元素更好的散列,将hash值的高位也利用起来
  举个例子
  如果我们不对hashCode进行按位异或,直接将hash和length1进行按位与运算就有可能出现以下的情况
  如果下一次生成的hashCode值高位起伏很大,而低位几乎没有变化时,高位无法参与运算
  可以看到,两次计算出的hash相等,产生了hash冲突
  所以无符号右移16位的目的是使高混乱度地区与地混乱度地区做一个中和,提高低位的随机性,减少哈希冲突
  文章中出现的关于面试题的错误请在评论区指出,我再进行改正优化。如果文章对你有所帮助,请给免费的赞吧,感谢大家。
  来源:blog。csdn。netwenwenaier?typeblog
投诉 评论 转载

买i9款华为MateBook16s的人,需要的是什么?HUAWEIMateBook16s虽然不是华为最旗舰的笔记本产品,但绝对算得上是华为迄今性能最好的一款轻薄式笔记本。它用的是第12代IntelCore平台,有三个处理器版……草原漫步从内蒙古草原到云冈石窟《草原漫步》作者:骆晓戈时值20世纪的九十年代,我在《小溪流》杂志社任主编,在多次中国少儿报刊协会的年会上认识了内蒙古的儿童刊物主编梅花。当时两家刊物互相赠送期刊,……漂亮女机器人走红,颜值直逼当红女明星,内部结构令人直冒冷汗网络给人们带来的便利,相信大家都体会到有多么香了。如今,许多国家都在致力于发展科技力量,最有代表性的就是机器人了。(此处已添加小程序,请到今日头条客户端查看)今天小……HashMap夺命14问,你能坚持到第几问?1。HashMap的底层数据结构是什么?在JDK1。7中和JDK1。8中有所区别:在JDK1。7中,由数组链表组成,数组是HashMap的主体,链表则是主要为了解决……广东哪道菜最好吃?经过推荐,这5道粤菜比较出名,你吃过几种?说到饮食文化,我国可以排在世界的前茅,不仅有很多烹饪方式,不同地区还有自己独特的美食,发展至今,还有了特别有名的八大菜系。八大菜系有着众多的菜肴,想要一下子讲完是不可能的……中国冰壶选手火出圈!身形高挑肤白貌美,帅气老公喊话冬奥后成婚最近一段时间,北京冬奥会的比赛依旧在顺利进行中,在冰壶的赛场上出现了非常多优秀的选手,而中国队的冰壶运动员也得到了很多网友的关注,中国冰壶队的选手们长相都非常出色,他们在赛场上……红了!霜叶红于二月花的初冬绝美色彩,已经在余杭的调色盘内调好不知不觉之间,冬天已悄然而至。黄的湖面、暖阳的倒影、缤纷的彩林对于痴迷红叶的游人们,这应该是最好的季节。今天,周小柚就推荐几处余杭的五彩之森,一起去看看斑斓世界吧!良渚文……房地产利好不断,万科又能融钱了,首期发行不超过50亿元!最近房地产行业出台了一系列利好政策,可为何各家房企的销售数据,依然不及往年?国家统计局发布2022年111月商品房行业数据。2022年111月,全国商品房销售面积12。1……纵目科技中国自动驾驶上市第一股?11月23日,自动驾驶初创公司纵目科技申请上市获科创板受理。如果成功,纵目将成为中国自动驾驶第一股,意义重大。它将成为第一份国内自动驾驶初创公司的可研究样本。透过它……华为nova系列发展史主角影像【1】华为nova处理器:骁龙6255英寸60HzLCD直屏3020mAh18W有线充电后置1200万主摄;前置800万像素镜头后置指纹解锁,有极……数读去年同期基数较低,今年4月前9天乘用车市场零售同比增47文:懂车帝原创李德喆〔懂车帝原创行业〕懂车帝从乘联会了解到,日前,乘联会发布了今年4月1日至4月9日乘用车市场销售情况。4月1日4月9日,乘用车市场零售销量达32。……痛心!男童在景区吊桥上意外落水,父亲下水营救不幸溺亡4月9日,江苏扬州一景区内发生意外事故,一男童意外落水,其父亲下水营救时不幸溺亡。目前,相关部门已勒令景区相关项目停业。一段现场搜救视频显示……
今晚9点半,美国1月CPI数据反通胀进程或遇考验?环保督察组曝光!三峡库区康养项目竟成采石场,挖山采石600余官宣!库里常规赛报销,艾顿续约心理价曝光,西蒙斯拒绝千万合同广州市天河区著名的城中村,名字很好听,却因位置偏远而不受重视羽毛球十二个基本动作带娃去这些地方,违法!很多家长不知道美媒将湖人勇士202223赛季的首发替补等进行分析对比,谁更完美主义人生最大诅咒!为什么你总是失败?俞灏明遭知名博主控诉,过往恋情被扒引热议!下一个塌房会是他吗财联社创投通3月份Web3。0融资数量总额双双下滑中国投资机第五届中国互联网辟谣优秀作品网络投票进行中!快来为江苏入围作玛雅文明被吹的那么神,竟然被西班牙团灭,来看下玛雅的发展吧中考满分作文我想握住你的手茶叶称为人们全方位的营养保健大师酒后说话的真与假老人夏季如何吃生拌有机蔬菜萝卜丝饼400字作文鱿鱼游戏面具人是怎么被选中的?他们跟参赛者一样,都是游戏人小三诱惑男人竟爱用这招数2023年除夕可以过生日吗小学生课堂纪律(如何维持课堂纪律)新商标法实施条例修改的内容有哪些校外教育培训如何从无序走向规范谢教授语录20200106

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找美丽时装彩妆资讯历史明星乐活安卓数码常识驾车健康苹果问答网络发型电视车载室内电影游戏科学音乐整形