HashMap:
- JDK1.7中使用一个Entry数组来存储数据,用key的hashcode取模来决定key会被放到数组里的位置,如果hashcode取模结果相同,那么这些key会被定位到Entry数组的同一个格子里,这些key会形成一个链表
- JDK1.8中使用一个Node数组来存储数据,但这个Node可能是链表结构,也可能是红黑树结构,如果插入的key的hashcode取模结果相同,那么这些key也会被定位到Node数组的同一个格子里,如果同一个格子里的key不超过8个,使用链表结构存储,如果超过了8个,那么会调用treeifyBin函数,将链表转换为红黑树
HashTable:
- hashmap中key和value均可以为null,但是hashtable中key和value均不能为null
- hashmap采用的是数组(桶位)+链表+红黑树结构实现,而hashtable中采用的是数组(桶位)+链表实现
- hashmap中出现hash冲突时,如果链表节点数小于8时是将新元素加入到链表的末尾,而hashtable中出现hash冲突时采用的是将新元素加入到链表的开头
- hashmap中数组容量的大小要求是2的n次方,如果初始化时不符合要求会进行调整,必须为2的n次方,而hashtable中数组容量的大小可以为任意正整数
- hashmap中的寻址方法采用的是位运算按位与,而hashtable中寻址方式采用的是求余数
- hashmap不是线程安全的,而hashtable是线程安全的,hashtable中的get和put方法均采用了synchronized关键字进行了方法同步
- hashmap中默认容量的大小是16(扩容因子是0.75,2倍扩容,条件:1.当前个数是否大于等于阈值),而hashtable中默认数组容量是11(扩容因子是0.75,2倍+1扩容)
ConcurrentHashMap:
- jdk1.7中采用Segment + HashEntry的方式进行实现,Segment在实现上继承了ReentrantLock,这样就自带了锁的功能
- jdk1.8中放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全
- jdk1.8中ConcurrentHashMap的get操作上面并没有加锁,所以在多线程操作的过程中,并不能完全的保证一致性,这里和1.7当中类似,是弱一致性的体现
- jdk1.8中ConcurrentHashMap的size操作与JDK1.7里面的实现不同,1.7里面使用了加锁的方式实现,JDK1.8中对于每个table[i]做了求和之后就返回,可以看出JDK1.8牺牲了精度,来换取更高的效率