HashMap与HashTable的区别
-
HashMap线程不安全,效率高
-
HashMap是Hashtable的轻量级实现,他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable。
-
HashTable线程安全,效率低;
-
HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75。
-
HashMap扩容时是当前容量翻倍即:capacity x 2,Hashtable扩容时是容量翻倍+1即:capacity x 2+1。
-
底层原理比较
-
HashMap:
-
JDK 1.8的 改变
-
HashMap采用了数组+链表+红黑树的实现,当链表长度超过阈值(8)时,将链表转换为红黑树。(红黑树查询删除快新增慢)在性能上进一步得到提升。
-
JDK1.7在扩容时是直接头插法倒叙排一遍
-
当 Hash 冲突严重时,在桶上形成的链表会变的越来越长,这样在查询时的效率就会越来越低;时间复杂度为
O(N)
。
-
-
jdk1.8 HashMap在扩容时保持了原来链表中的顺序
-
底层是数组加连表实现
-
数组:数组的特点是:寻址容易,插入和删除困难;
-
链表:寻址困难,插入和删除容易。
-
解决hash冲突
-
开放定址法(线性探测再散列,二次探测再散列)
-
再哈希法
-
链地址法
-
建立一个公共溢出区
-
-
*ConcurrentHashMap
-
jdk1.7中 也是由 Segment 数组、HashEntry 组成,和 HashMap 一样,仍然是数组加链表。
-
由于 HashEntry 中的 value 属性是用 volatile 关键词修饰的,保证了内存可见性,所以每次获取时都是最新值。
ConcurrentHashMap 的 get 方法是非常高效的,因为整个过程都不需要加锁。
-
缺点
-
那就是查询遍历链表效率太低。
-
-
jdk1.8中 其中抛弃了原有的 Segment 分段锁,而采用了
CAS + synchronized
来保证并发安全性。 -
也将 1.7 中存放数据的 HashEntry 改为 Node,但作用都是相同的。
其中的
val next
都用了 volatile 修饰,保证了可见性 -
改动之处:
-
采用红黑树之后可以保证查询效率(
O(logn)
),甚至取消了 ReentrantLock 改为了 synchronized,这样可以看出在新版的 JDK 中对 synchronized 优化是很到位的。
-
三者之间的联系 ** HashMap**&&HashTable&&ConcurrentHashMap**
-
在并发编程中使用HashMap可能导致程序死循环。而使用线程安全的HashTable效率又非常低下,基于以上两个原因,便有了ConcurrentHashMap的登场机会
-
线程不安全的HashMap
-
在多线程环境下,使用HashMap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap。HashMap在并发执行put操作时会引起死循环,是因为多线程会导致HashMap的Entry链表形成环形数据结构,一旦形成环形数据结构,Entry的next节点永远不为空,就会产生死循环获取Entry。
-
-
效率低下的HashTable
-
HashTable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下HashTable的效率非常低下。因为当一个线程访问HashTable的同步方法,其他线程也访问HashTable的同步方法时,会进入阻塞或轮询状态。如线程1使用put进行元素添加,线程2不但不能使用put方法添加元素,也不能使用get方法来获取元素,所以竞争越激烈效率越低。
-
-
ConcurrentHashMap的锁分段技术可有效提升并发访问率
-
HashTable容器在竞争激烈的并发环境下表现出效率低下的原因是所有访问HashTable的线程都必须竞争同一把锁,假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术。首先将数据分成一段一段地存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问.
-
-
*ConcurrentHashMap
-
jdk1.7中采用
Segment
+HashEntry
的方式进行实现 -
Segment是一种可重入锁(ReentrantLock)(数组加链表实现)
-
jdk1.8中取消了Segment分段锁的数据结构,取而代之的是数组+链表(红黑树)的结构,因此在链表节点数量大于8时,会将链表转化为红黑树进行存储。这样一来,查询的时间复杂度就会由原先的O(n)变为O(logN)。下面是其基本结构:
-
取而代之的是采用
Node
+CAS
+Synchronized
来保证并发安全进行实现,
-