影响hashmap 性能有两个因素:初始容量和加载因子。 初始容量是16 也就是有16个桶(16个Entry数组),加载因子默认是0.75,默认存储空间为2的30次方大小。当hash表中的条目数大于加载因子与当前容量乘积时(0.75*16=12)时,容量扩大为2倍(也就是扩展到24)。为什么容量一定是2的n次方呢,因为这里做了移位运算(移位运算就是2的n次方)。
其次第二个问题:hsahmap的数据结构是怎样的呢? 是数组+单链表。2^n转换成二进制就是1+n个0(10000000...),2^n-1转化为二进制就是0+n个1.
HashTable中的实现对容量的大小没有规定,最终的bucketIndex是通过取余来运算的。
默认加载因子 (.75) 在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本。初始容量默认为在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地降低 rehash 操作次数。如果初始容量大于最大条目数除以加载因子(实际上就是最大条目数小于初始容量*加载因子),则不会发生 rehash 操作。到达临界值(阀值)threshold时,就要对Entry数组扩容,这是Java集合类框架最大的魅力,HashMap在扩容时,新数组的容量将是原来的2倍,由于容量发生变化,
原有的每个元素需要重新计算bucketIndex,再存放到新数组中去,也就是所谓的rehash。
那在多线程下使用HashMap我们需要怎么做,几种方案:
- 在外部包装HashMap,实现同步机制
- 使用Map m = Collections.synchronizedMap(new HashMap(...));,这里就是对HashMap做了一次包装
- 使用java.util.HashTable,效率最低
- 使用java.util.concurrent.ConcurrentHashMap,相对安全,效率较高