当前位置: 首页 > 科技观察

为什么HashMap的加载因子初始值为0.75?这篇文章用最通俗的方式告诉你答案

时间:2023-03-12 13:56:28 科技观察

之前写过一篇关于HashMap的文章,反响很好,但是评论区问的最多的问题是为什么HashMap的负载因子初始值为0.75,私下好好研究了一下,总结了这篇文章。本文基于JDK1.8,在此进行说明。HashMap(基于jdk1.8源码分析,也是我最好的回应,不要错过)OK。下面开始分析。1.负载因子的作用对于HashMap的研究,我一直在思考源码是如何实现的。现在再看的时候发现系统默认的参数值才是HashMap的精髓。负载因子与扩容机制有关,也就是说如果当前容器的容量达到我们设置的最大值,就会开始扩容操作。举个例子解释一下,免得大家看不懂:比如当前容器容量为16,负载因子为0.75,16*0.75=12,也就是说当容量达到12时,扩容操作会被执行。他的功能很简单,相当于一个扩容机制的门槛。当超过这个阈值时,就会触发扩容机制。HashMap源码已经默认为我们指定了0.75的加载因子。publicclassHashMapextendsAbstractMapimplementsMap,Cloneable,Serializable{//略staticfinalfloatDEFAULT_LOAD_FACTOR=0.75f;//略publicHashMap(initialCapacity,floatloadFactor){if(initialCapacity<0)thrownewIllegalArgumentException("Illegalinitialcapacity:"+initialCapacity);if(initialCapacity>MAXIMUM_CAPACITY)initialCapacity=MAXIMUM_CAPACITY;if(loadFactor<=0||Float.isNaN(loadFactor))thrownewIllegalArgumentException("IllegalArgumentException("非法负载因子:"+loadFactor);this.loadFactor=负载因子阈值=tableSizeFor(initialCapacity);}//略}我截取了部分源码,从中可以看出系统默认的负载因子值为0.75,我们也可以在构造方法中指定。下面正式分析一下为什么是默认的0.75。2.原因解释(强调)当我们考虑HashMap时,首先应该想到的是HashMap只是一种数据结构。既然是数据结构,最重要的就是节省时间和空间。加载因子的作用肯定也是节省时间和空间。为什么要保存?我们考虑两种极端情况。1.负载系数为1.0。我们先来看看HashMap的底层数据结构。我们的数据一开始就存放在一个数组中。当发生哈希冲突时,会在该数据节点上生成一个链表。当链表达到一定长度时,当长度很长时,链表就会转为红黑树。当加载因子为1.0时,表示只有当数组的8个值(本图为8)都被填充时,才会发生扩容。这就带来了一个很大的问题,因为Hash冲突是无法避免的。当负载因子为1.0时,意味着会出现大量的Hash冲突,底层的红黑树变得异常复杂。对查询效率极为不利。在这种情况下,牺牲时间来保证空间利用率。所以,一句话,负载率太大。虽然空间利用率提高了,但是时间效率却降低了。2、载荷系数为0.5。当加载因子为0.5时,表示当数组中的元素达到一半时,就会开始扩容。由于填充的元素少了,Hash冲突也会减少,所以底层链表的长度或者红黑树的高度都会变小。查询效率会提高。不过兄弟们,这个时候空间利用率会大大降低。原本存储1M数据,现在意味着需要2M空间。一句话,负载率太小了。虽然提高了时间效率,但降低了空间利用率。3、负载系数为0.75。经过前面的分析,基本就知道为什么是0.75的答案了。这是时间和空间之间的权衡。当然这个答案不是我自己的。答案在源代码中,我们可以看一下:/*

作为一般规则,默认的加载因子(.75)在时间和空间成本之间提供了一个很好的*权衡。较高的值会减少*空间开销,但会增加查找成本(反映在大多数*HashMap类的操作中,包括*getandput).Theexpectednumberofentriesin*themapanditsloadfactorshouldbetakenintoaccountwhen*settingitsinitialcapacity,soastominimizethenumberof*rehashoperations.Iftheinitialcapacityisgreaterthanthe*maximumnumberofentriesdividedbytheloadfactor,norehash*operationswilleveroccur.*/大致意思就是说负载因子是0.75的时候,空间利比比较高,并且避免了相当多的Hash冲突,使得底层链表或者红黑树的高度相对较低,提高了空间效率。OK,写到这里答案基本就出来了,可以一句话总结成一篇文章了。如有问题,请批评指正。本文转载自微信公众号“愚公要移山”,可关注下方二维码。转载本文请联系愚公移山公众号。