创建hashmap对象方法如下:
Map<String, Object> map = new HashMap<String, Object>(size);
在平时coding中,put元素个数很少,加上偷懒。size往往就忽略不写了。
但这次put 15个元素之多..., 我决定要给size赋值了。
赋多少呢?
hashmap默认容量是16,但是当元素达到0.75(也称负载因子)时,即16*0.75=12,hashmap会自动扩容2倍。
那么意味着,size值 = 15 *4 /3 + 1= 21,才不会去触发resize动作。虽然我把size设置为21了,但是jdk在编译优化时,
会把size设为 32,即2的5次方。
也许你注意到了,size最好为2的幂次方,为什么呢? 答应是:保证hash值分布均匀。
因为hashmap的底层数据结构是数组,假如size为16. 那么元素为
hash = (length-1) & hashcode
16 -1 = 15,即二进制1111,和hashcode与操作运算,即hashcode本身,那么保证了hashcode的分布均匀,即保证了hash值的唯一可能性就很大。
那么并不是100%唯一,那么hash值就可能相同,我们叫hash值碰撞,意味着数组中某个元素,比如hash值为2, 2这个元素就以链表形式会保存m个元素,m为桶的深度,即被碰撞的次数。
查询效率就变低,时间复杂度为 o(1) + o(m),其中,o(1)为hashcode,o(m)为equal比较时间。
既然hashmap resize扩展,要重新计算每个元素在数组中的位置,那么肯定是非常消耗性能的。
为此,我单独测试了下。1个是没有设置初始化容量,1个是设置了初始化容量。
多次测试下,运行结果并不是那么的明显。
比如:size为1千万
未初始化下,运行7s左右
初始化下,在6s到12s之间。
这可百思不得其解了。
我想,单从时间这个维度,可能是很难发现差异性。冷静想想自动扩容的实现步骤,设置初始大小肯定有利于性能。
自动扩容内存得重分一次,那在还没有GC前,会导致其实用了很多内存的,而GC也浪费时间,这样复制来复制去,也浪费CPU计算资源。