HashTable,HashMap,ConcurrentHashMap区别和联系

首先比较HashMap和HashTable:

HashMap:

1、底层为数组加链表实现,允许key为null
2、线程不安全
3、初始容量为16,扩容:newsize = oldsize*2,size一定为2的n次幂
4、插入元素后才判断该不该扩容,有可能无效扩容(插入后如果扩容,如果没有再次插入,就会产生无效扩容)
5、当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀,初始加载因子为0.75
6、计算index方法:index = hash & (tab.length – 1)

HashTable:

1、底层为数组加链表实现,不允许key为null
2、实现线程安全的方法是在修改数据时锁住整个HashTable,效率低
3、初始size为11,扩容:newsize = olesize*2+1
4、计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length

  HashMap和Hashtable都是用hash算法来决定其元素的存储,因此HashMap和Hashtable的hash表包含如下属性:

-. 容量(capacity):hash表中桶的数量
-. 初始化容量(initial capacity):创建hash表时桶的数量,HashMap允许在构造器中指定初始化容量
-. 尺寸(size):当前hash表中记录的数量
-. 负载因子(load factor):负载因子等于“size/capacity”。负载因子为0,表示空的hash表,0.5表示半满的散列表,依此类推。轻负载的散列表具有冲突少、适宜插入与查询的特点(但是使用Iterator迭代元素时比较慢)
  除此之外,hash表里还有一个“负载极限”,“负载极限”是一个0~1的数值,“负载极限”决定了hash表的最大填满程度。当hash表中的负载因子达到指定的“负载极限”时,hash表会自动成倍地增加容量(桶的数量),并将原有的对象重新分配,放入新的桶内,这称为rehashing。
  HashMap和Hashtable的构造器允许指定一个负载极限,HashMap和Hashtable默认的“负载极限”为0.75,这表明当该hash表的3/4已经被填满时,hash表会发生rehashing。
  “负载极限”的默认值(0.75)是时间和空间成本上的一种折中:
较高的“负载极限”可以降低hash表所占用的内存空间,但会增加查询数据的时间开销,而查询是最频繁的操作(HashMap的get()与put()方法都要用到查询),较低的“负载极限”会提高查询数据的性能,但会增加hash表所占用的内存开销,程序猿可以根据实际情况来调整“负载极限”值。

ConcurrentHashMap

1、底层采用分段的数组+链表实现,线程安全,实现线程安全的方式是分段加锁
2、线程安全的实现方式是通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新的值。)
3、Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术,ConcurrentHashMap也是使用synchronized进行加锁的,ConcurrentHashMap默认将hash表分为16个桶,诸如get、put、remove等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。


image.png

4、有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁
5、扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容


image.png

你可能感兴趣的:(HashTable,HashMap,ConcurrentHashMap区别和联系)