目录
HashMap和HashTable
HashMap和HashTable的主要区别:
1.安全性
Hashtable是线程安全,HashMap是非线程安全。HashMap的性能会高于Hashtable,我们平时使用时若无特殊需求建议使用HashMap,在多线程环境下若使用HashMap需要使用Collections.synchronizedMap()
方法来获取一个线程安全的集合(Collections.synchronizedMap()
实现原理是Collections定义了一个SynchronizedMap
的内部类,这个类实现了Map接口,在调用方法时使用synchronized
来保证线程同步
2.是否可以使用null作为key
HashMap可以使用null作为key,不过建议还是尽量避免这样使用。HashMap以null作为key时,总是存储在table数组的第一个节点上。而Hashtable则不允许null作为key
3.继承了什么,实现了什么
HashMap继承了AbstractMap,HashTable继承Dictionary抽象类,两者均实现Map接口
4.默认容量及如何扩容
HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75。HashMap扩容时是当前容量翻倍即:capacity
2
,Hashtable扩容时是容量翻倍+1即:capacity
(2+1)
6.底层实现
HashMap和Hashtable的底层实现都是数组
+链表
结构实现
7.计算hash的方法不同
Hashtable计算hash是直接使用key的hashcode对table数组的长度直接进行取模
HashMap计算hash对key的hashcode进行了二次hash,以获得更好的散列值,然后对table数组长度取模
HashTable现阶段已经被淘汰,我们日常开发中使用HashMap即可。
ConcurrentHashMap
在多线程环境下,使用HashMap进行put操作时存在丢失数据的情况,为了避免这种bug的隐患,强烈建议使用ConcurrentHashMap代替HashMap。HashTable使用synchronized来锁住整张Hash表来实现线程安全,即每次锁住整张表让线程独占,相当于所有线程进行读写时都去竞争一把锁,导致效率非常低下。ConcurrentHashMap在此基础上做出了优化。
1)ConcurrentHashMap使用了”锁桶“的方式来代替”一把全局锁“,有效降低锁冲突的概率。
结构如下:
哈希表数组里的每一个链表都加了一个锁。看起来锁对象多了,实际上不会产生更多的开销,原因是java中的任何一个对象都可以作为锁对象,直接拿每个链表的头结点创建的线程对象作为锁对象就行了。
另一方面,如果两个线程针对不同的链表进行操作,不会涉及到锁冲突。synchronized如果不产生锁冲突,就是个偏向锁,偏向锁非常轻量,所以效率有很大提升。
因为操作两个不同链表上的元素,没有修改”公共变量“,本身就不会涉及到线程安全问题。
2)引入CAS
当我们修改hash表中的公共变量比如size时,会涉及到多个线程修改同一个变量。这时候ConcurrentHashMap通过引入CAS的方式,来修改size,避免了加锁操作,提高了效率。
3)针对扩容操作做了特殊优化
当负载因子太大的时候,Hash表就要扩容。扩容本身是一个比较低效的操作。对于使用有非常大的影响。ConcurrentHashMap会在扩容的时候,搞两份空间。
一份是扩容之前的空间,一份是扩容之后的空间。接下来的每次进行hash表的基本操作,都会把一部分数据从旧空间搬运到新空间。不是一口气搬完,而是分多次搬。
搬的过程中,如果是 1)插入操作 ---> 插入到新的空间上面。
2)删除操作 -----> 新的旧的都要删除
3)查找操作 ------> 新的旧的都要查找
以上关于ConcurrentHashMap,希望对你有所帮助。