HashMap和ConcurrentHashMap
HashMap
结构
桶数组+单链表+红黑树(JDK1.8引入)
容量是2的幂的原因
寻找位置时,(n - 1)& hashCode值等价于hash%n,但是&比%具有更高的效率。
得到key的hashCode值后,通过二次hash(第一次hash时右移 16 位,hashCode值高16位与低16位异或操作,高16位保持不变;第二次hash时(n - 1)& hashCode值)来减少冲突。
异或操作指,值不同时是1,值相同时是0。
属性
1 树形化阈值
单链表长度达到8时把该单链表转换成红黑树,因为结点数较多时红黑树查询效率优于单链表。
2 反树形化阈值
红黑树结点数减少到6时把该红黑树转换成单链表,因为单链表结点比红黑树结点更省空间,而且结点较少时单链表查询效率高。
3 最小树形化数组容量阈值
单链表需要转换成红黑树时,如果桶数组容量<64,则不执行转换,通过扩容来处理桶数组容量太小(小于64)导致的hash冲突。
4 默认负载因子0.75
负载因子=key总数/桶总数。这是时间和空间成本上的折中方案。负载因子高虽然减少空间成本,但会增加查询时间成本。负载因子低虽然减小查询时间成本,但会增加空间成本,频繁的扩容会降低性能。
扩容机制即resize方法
返回重组结点后的新的桶数组
桶数组旧容量的2倍作为桶数组新容量,键值对数量旧阈值的2倍作为键值对数量新阈值。
重组结点时使用尾插法,保证结点的相对次序与原来相同。
ConcurrentHashMap
为什么key和value均为非空?(Hashtable也是如此)
key非空:putVal方法中通过key的hashCode方法来获取散列码。
value非空:作为支持并发的类,调用get方法获得指定key对应的value时,如果为空,则无法判断产生的原因即put操作还是不存在该key。对于不支持并发的HashMap,可以通过调用contains方法来判断;对于支持并发的Map,调用contains方法(存在该key)和调用get方法(不存在该key)的对象已经不同,即不能通过contains方法来判断是否存在该key,下一时刻map可能会变。
1.7基本结构
1.8改进(整体结构与HashMap类似)
取消segments属性,通过桶数组来保存数据。桶数组元素对应的单链表或红黑树首结点作为锁,相当于对每一行数据加锁,进一步减小锁粒度。
桶数组+单向链表的数据结构变为桶数组+单链表+红黑树。