下面是HashMap的一个构造函数,两个参数initialCapacity,loadFactor数组
这关系HashMap的迭代性能。数据结构
1 /** 2 * Constructs an empty <tt>HashMap</tt> with the specified initial 3 * capacity and load factor. 4 * 5 * @param initialCapacity the initial capacity 6 * @param loadFactor the load factor 7 * @throws IllegalArgumentException if the initial capacity is negative 8 * or the load factor is nonpositive 9 */ 10 public HashMap(int initialCapacity, float loadFactor) { 11 if (initialCapacity < 0) 12 throw new IllegalArgumentException("Illegal initial capacity: " + 13 initialCapacity); 14 if (initialCapacity > MAXIMUM_CAPACITY) 15 initialCapacity = MAXIMUM_CAPACITY; 16 if (loadFactor <= 0 || Float.isNaN(loadFactor)) 17 throw new IllegalArgumentException("Illegal load factor: " + 18 loadFactor); 19 this.loadFactor = loadFactor; 20 this.threshold = tableSizeFor(initialCapacity); 21 }
关于这两个参数值的设定界限:函数
1. initialCapacity是map的初始化容量,initialCapacity > MAXIMUM_CAPACITY,代表map的最大容量是1<<30,也就是1左移30位,每左移一位乘以2,因此就是1*2^30=1073741824.性能
2. loadFactor是map的负载因子,loadFactor <= 0 || Float.isNaN(loadFactor),代表负载因子要大于0,且是非无穷大的数字this
负载因子为何会影响HashMap性能spa
首先回忆HashMap的数据结构,code
咱们都知道有序数组存储数据,对数据的索引效率都很高,可是插入和删除就会有性能瓶颈(回忆ArrayList),blog
链表存储数据,要一次比较元素来检索出数据,因此索引效率低,可是插入和删除效率高(回忆LinkedList),索引
二者取长补短就产生了哈希散列这种存储方式,也就是HashMap的存储逻辑.ci
而负载因子表示一个散列表的空间的使用程度,有这样一个公式:initailCapacity*loadFactor=HashMap的容量。
因此负载因子越大则散列表的装填程度越高,也就是能容纳更多的元素,元素多了,链表大了,因此此时索引效率就会下降。
反之,负载因子越小则链表中的数据量就越稀疏,此时会对空间形成烂费,可是此时索引效率高。
如何科学设置 initailCapacity,loadFactor的值
HashMap有三个构造函数,能够选用无参构造函数,不进行设置。默认值分别是16和0.75.
官方的建议是initailCapacity设置成2的n次幂,laodFactor根据业务需求,若是迭代性能不是很重要,能够设置大一下。
为何initailCapacity要设置成2的n次幂,网友解释了,我以为很对,如下摘自网友博客:深刻理解HashMap
左边两组是数组长度为16(2的4次方),右边两组是数组长度为15。两组的hashcode均为8和9,可是很明显,当它们和1110“与”的时候,产生了相同的结果,也就是说它们会定
位到数组中的同一个位置上去,这就产生了碰撞,8和9会被放到同一个链表上,那么查询的时候就须要遍历这个链表,获得8或者9,这样就下降了查询的效率。同时,咱们也能够
发现,当数组长度为15的时候,hashcode的值会与14(1110)进行“与”,那么最后一位永远是0,而0001,0011,0101,1001,1011,0111,1101这几个位置永远都不能
存放元素了,空间浪费至关大,更糟的是这种状况中,数组可使用的位置比数组长度小了不少,这意味着进一步增长了碰撞的概率,减慢了查询的效率!
因此说,当数组长度为2的n次幂的时候,不一样的key算得得index相同的概率较小,那么数据在数组上分布就比较均匀,也就是说碰撞的概率小,相对的,查询的时候就不用
遍历某个位置上的链表,这样查询效率也就较高了。
resize()方法
initailCapacity,loadFactor会影响到HashMap扩容。
HashMap每次put操做是都会检查一遍 size(当前容量)>initailCapacity*loadFactor 是否成立。若是不成立则HashMap扩容为之前的两倍(数组扩成两倍),
而后从新计算每一个元素在数组中的位置,而后再进行存储。这是一个十分消耗性能的操做。
因此若是能根据业务预估出HashMap的容量,应该在建立的时候指定容量,那么能够避免resize().