HashMap、ConcurrentHashMap要点记一下

HashMap

jdk1.7与1.8的共同点和差异点

共同点:

  1. 线程不安全
  2. 每次put时,都会判断是否需要扩容。
  3. hashmap的容量只能是2的n倍,当初始化不为2的倍数时,默认会创建大于该初始值的2的倍数容量大小。为了减少hash碰撞与提高运算性能用位运算。
  4. 属性:默认初始化容量为16,负载因子为0.75(即容量为12=16*0.75会自动扩容)

差异点:

jdk1.7——
  1. 数据结构结合了数组和链表的特点,默认为size为16的entry[],当发生hash冲突时,在对应entry下形成链表结构。链表的时间复杂度为O(n)
  2. 添加数据时先扩容后添加
  3. 添加数据的时候,采用头插法进行添加(方便查询,链表查询时会先二分,之后分别进行遍历,越靠前越容易查询)。并发情况下,会产生链表死循环。
  4. 当第一次添加的时候,进行空间分配数组填充。
  5. 扩容时,hash重新计算。
jdk1.8——
  1. 数据结构结合了数组和链表的特点,默认为size为16的Node[],当发生hash冲突时,在对应Node下形成链表结构。当链表过长,即长度大于8时。1.判断是否需要转换成红黑树,转换红黑树前,再判断Node数组与MIN_TREEIFY_CAPACITY=64比较,如果小于,先进行2倍扩容解决,大于,则进行红黑树转换。链表的时间复杂度为O(n),红黑树的时间复杂度为O(logn)。
  2. 添加数据时先添加再扩容
  3. 添加数据采用尾插法,红黑树也可能会产生死循环。并发情况下,计算size大小时不安全,hash冲突计算前后并发可能发生覆盖数据。
  4. 当第一次添加时,直接先扩容。
  5. 使用高低位索引机制计算hash,hash算法更散列,减小hash冲突。
  6. 扩容时,hash二进制多取一位,即扩容后,链表上的元素,要么在本索引下,要么在该索引+原数组长度,减少了hash次数,提升性能。

引申


为啥要链表长度大于8且数组长度大于64才进行树化?

链表节点太少没必要转换数据结构,因为转化的过程需要耗费时间与空间。

为什么不直接一直用红黑树呢?

因为树的结构太浪费空间,只有节点足够多的情况下用树才能体现出它的优势,而如果在节点数量不够多的情况下用链表的效率更高,占用的空间更小。


ConcurrentHashMap

jdk1.7与1.8的共同点和差异点

共同点:

  1. 线程安全,具备hashMap的特性

差异点:

jdk1.7——
  1. ReentrantLock+Segment+HashEntry的结构
  2. 锁的粒度基于segment
  3. segment默认容量为16
jdk1.8——
  1. synchronized+CAS+HashEntry+红黑树
  2. 锁头Node
  3. 使用红黑树优化链表,同hashmap
  4. 使用synchronized代替reetrantLock

引申

为什么使用synchronized代替reetrantLock

  1. reetrantLock的优点是灵活,通过Condition来调控;而在低粒度的锁中,Condition相比于synchronized没有优势
  2. 在jdk团队的升级维护下,使用了锁升级的优化方式,一步步的从偏向锁到轻量级锁到最后的重量级锁,synchronized的性能不弱于reetrantLock,在低粒度的锁下有更好的表现

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值