HashMap和HashTable的比较:
区别:
1,HashMap的键值都可以是null,HashTable都不可以,所以HashMap中多用contains(key)这个方法判断是否存在这个key
2,HashTable中存在synchronized,存在同步锁,这就是区分两者线程是否安全的关键
3,HashMap中迭代器是fail-fast迭代器,HashTable为fail-safe迭代器。
注:fail-safe允许在遍历的过程中对容器中的数据进行修改,而fail-fast则不允许。
fail-fast:直接在容器上进行遍历,在遍历过程中,一旦发现容器中的数据被修改了,会立刻抛出ConcurrentModificationException异常导致遍历失败。常见的的使用fail-fast方式遍历的容器有HashMap和ArrayList等。
fail-safe:这种遍历基于容器的一个克隆。因此,对容器内容的修改不影响遍历。常见的的使用fail-safe方式遍历的容器有ConcerrentHashMap和CopyOnWriteArrayList等。
4,HashTable保证了线程安全,所以运行速度相对会慢一些
相同点:
1.都是插入后扩容,扩容触发条件为当前有效村粗数组的hash值个数超过总容量75/100(默认为75/100)
2. HashTable和HashMap的底层实现均是数组 + 链表
HashTable的初始化 size = 11,扩容 newsize = oldsize2 + 1,
计算index的方式是 index = (hash & 0x7FFFFFFF) % tab.length。
HashMap的初始化 szie = 16, 扩容 newsize = 2oldsize(size是2的 n 次方),
计算index的方式是 index = hash & (tab.length – 1)
ConcurrentHashMap:
是对HashMap的优化,线程安全,使用了锁,并且是分段锁
1.底层实现是链表+分段数组
2.默认将Map分为16片(segment,可调)
3. ConcurrentHashMap是插入前扩容,即插入前检查当前有效存储数组的hash值个数是否超过负载因子(默认为总容量的75%),若超过,再扩容
注:分段锁:
是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。
ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7与JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。
当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在那一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。
但是,在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。
分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。