天天看点

HashMap、HashTable、ConcurrentHashMap简述

HashMap:

  1. JDK1.7中使用一个Entry数组来存储数据,用key的hashcode取模来决定key会被放到数组里的位置,如果hashcode取模结果相同,那么这些key会被定位到Entry数组的同一个格子里,这些key会形成一个链表
  2. JDK1.8中使用一个Node数组来存储数据,但这个Node可能是链表结构,也可能是红黑树结构,如果插入的key的hashcode取模结果相同,那么这些key也会被定位到Node数组的同一个格子里,如果同一个格子里的key不超过8个,使用链表结构存储,如果超过了8个,那么会调用treeifyBin函数,将链表转换为红黑树

HashTable:

  1. hashmap中key和value均可以为null,但是hashtable中key和value均不能为null
  2. hashmap采用的是数组(桶位)+链表+红黑树结构实现,而hashtable中采用的是数组(桶位)+链表实现
  3. hashmap中出现hash冲突时,如果链表节点数小于8时是将新元素加入到链表的末尾,而hashtable中出现hash冲突时采用的是将新元素加入到链表的开头
  4. hashmap中数组容量的大小要求是2的n次方,如果初始化时不符合要求会进行调整,必须为2的n次方,而hashtable中数组容量的大小可以为任意正整数
  5. hashmap中的寻址方法采用的是位运算按位与,而hashtable中寻址方式采用的是求余数
  6. hashmap不是线程安全的,而hashtable是线程安全的,hashtable中的get和put方法均采用了synchronized关键字进行了方法同步
  7. hashmap中默认容量的大小是16(扩容因子是0.75,2倍扩容,条件:1.当前个数是否大于等于阈值),而hashtable中默认数组容量是11(扩容因子是0.75,2倍+1扩容)

ConcurrentHashMap:

  1. jdk1.7中采用Segment + HashEntry的方式进行实现,Segment在实现上继承了ReentrantLock,这样就自带了锁的功能
  2. jdk1.8中放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全
  3. jdk1.8中ConcurrentHashMap的get操作上面并没有加锁,所以在多线程操作的过程中,并不能完全的保证一致性,这里和1.7当中类似,是弱一致性的体现
  4. jdk1.8中ConcurrentHashMap的size操作与JDK1.7里面的实现不同,1.7里面使用了加锁的方式实现,JDK1.8中对于每个table[i]做了求和之后就返回,可以看出JDK1.8牺牲了精度,来换取更高的效率