HashMap HashTable ConCurrentHashMap三者的区别
- 底层实现原理
HashMap 在Jdk1.7之前底层实现为数组+链表,通过哈希算法将元素的键(key)映射到数组槽位(bucket)中,如果多个键映射同一个槽位,它们会以链表的形式存储在同一个槽位上
HashTable 底层实现为数组+链表,数组为主体,链表是为了解决哈希冲突
ConcurrentHashMap 在jdk1.8之前采用的是分段的数组+链表,一个ConcurrentHashMap包含一个Segment数组,而一个Segment(扮演锁的角色)里包含一个HashEntry数组(用于存储键值对),每个HashEntry是一个链表结构的元素,因此遍历元素时较慢
在jdk1.8之后,采用数组+链表/红黑树,利用红黑树优化了之前的链表结构,一句话概述也就是:在利用在头结点加锁来保证线程安全,锁的粒度相比于Segment更小,且发生冲突和加锁的频率更低,并发操作的性能也就大大提升,时间复杂度也由O(n)->O(logn)
- 能不能存null键,null值
HashMap 可以存null键值对,只能有一个null键,因为键重复的话添加的时候会自动覆盖,可以有多个null值
HashTable 不可以存储null键值
- 扩容机制
HashMap 默认初始量为16,每次扩容2倍,如果插入元素后链表长度大于8的话,再判断数组长度是否大于64,如果大于64就将链表转为红黑树,反之扩容数组
- 线程安全
HashMap是线程不安全的,效率高一点
HashTable和ConcurrentHashMap线程安全,其中HashTable是将方法整体上锁同步,因此当多个线程进入的时候,需要等待锁内方法执行结束,效率较低
而ConcurrentHashMap在jdk1.8之前是利用分段锁,每个数组都分段上锁,在jdk1.8之后主要通过volatile + CAS 或者 synchronized实现线程安全