HashMap 和 ConcurrentHashMap 都是存key,value的。HashMap线程不安全,ConcurrentHashMap 线程安全。
HashMap
JAVA7结构如图,数组+链表:
HashMap 里面是一个数组,然后数组中每个元素是一个单向链表。上图中,每个绿色的实体是嵌套类 Entry 的实例,Entry 包含四个属性:key, value, hash 值和用于单向链表的 next。
HashMap根据key的hash()方法计算哈希值,即数组的index。如果有多个key的哈希值相同,第一个key在数组,第二个key链接第一个key,第三个key链接第二个key…
1. capacity:当前数组容量,始终保持 2^n,可以扩容,扩容后数组大小为当前的 2 倍。
2. loadFactor:负载因子,默认为 0.75
3. threshold:扩容的阈值,等于 capacity * loadFactor
HashMap的默认容量是16(capacity=16),负载因子默认0.75(loadFactor=0.75),如果数组的容量大于等于
capacity * loadFactor = 16 * 0.75= 12 则 HashMap的数组扩容,扩容后数组翻倍。如果扩容后容量不够还是按上述方法进行。
JAVA8结构如图:
Java8 对 HashMap 进行了一些修改,最大的不同就是利用了红黑树,所以其由 数组+链表+红黑树 组成
ConcurrentHashMap
HashMap 是线程不安全的,在多线程时 可以使用 HashTable,ConcurrentHashMap 。
HashTable是遗留类,很多映射的常用功能与 HashMap 类似,不同的是它承自 Dictionary 类,并且是线程安全的,但 HashTable 任意时间只能是一个线程访问,并发性不如 ConcurrentHashMap,因为 ConcurrentHashMap 引入了分段锁。建议线程用 ConcurrentHashMap 。
java7 ConcurrentHashMap 结构如下:
ConcurrentHashMap 和 HashMap 思路是差不多的,但是因为它支持并发操作,所以要复杂一些。整个 ConcurrentHashMap 由一个个 Segment 组成,Segment 代表”部分“或”一段“的意思,所以很多地方都会将其描述为分段锁。简单理解就是,ConcurrentHashMap 是一个 Segment 数组,Segment 通过继承ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全。Segment 可以理解为是一个HashMap。
Java8 对 ConcurrentHashMap 进行了比较大的改动,Java8 也引入了红黑树。如图: