HashMap和Hashtable的区别

何为HashMap

HashMap是在JDK1.2中引入的Map的实现类。

HashMap是基于哈希表实现的,每一个元素是一个key-value对,其外部通过单链表解决抵触问题,容量有余(超过了阀值)时,同样会主动增长。

其次,HashMap非线程平安的,只是用于单线程环境下,多线程环境下能够采纳concurrent并发包下的concurrentHashMap

如果不了解线程平安,能够看看我这篇文章:Java并发编程之多线程

HashMap 实现了Serializable接口,因而它反对序列化,实现了Cloneable接口,能被克隆。

HashMapkeyvalue都容许为nullkeynull的键值对永远都放在以table[0]为头结点的链表中。

何为Hashtable

Hashtable同样也是基于哈希表实现的,同样每个元素是一个key-value对,其外部也是通过单链表解决抵触问题,容量有余(超过了阀值)时,同样会主动增长

Hashtable也是JDK1.0引入的类,是线程平安的,能用于多线程环境中。

Hashtable同样实现了Serializable接口,它反对序列化,实现了Cloneable接口,能被克隆。

也就是说,这两个货色大部分时雷同的。

Hashtable与HashMap的不同

首先,从下面能够得出,线程平安是不同的。

  1. HashMap线程不平安,HashTable线程平安。
  2. 蕴含的contains办法不同,HashMap是没有contains办法的。
  3. Hashmap是容许key和value为null值的。
  4. 计算hash值形式不同。
  5. .扩容形式不同。
  6. 解决hash抵触形式不同。
  7. HashMap是继承自AbstractMap类,而Hashtable是继承自Dictionary类。
  8. 对外提供的接口不同,Hashtable比HashMap多提供了elements() 和contains() 两个办法。

如果你须要具体具体的理解不同,能够返回浏览器获取具体区别与原理。

ConcurrentHashMap作用

看看上面我箭头指的中央。

因为HashMap是线程不平安的,尽管Hashtable是线程平安的,可是他是一个被舍弃的类,既然淘汰了,那咱们就根本不必了。

那什么货色能够代替Hashtable成为HashMap的线程安全类呢?

concurrentHashMap能够用于并发环境,他是反对线程平安的。

线程不平安的HashMap,线程不平安的HashMap,促使了ConcurrentHashMap在JDK5诞生。

HashTable容器在竞争强烈的并发环境下体现出效率低下的起因,是因为所有拜访HashTable的线程都必须竞争同一把锁。那如果容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程拜访容器里不同数据段的数据时,线程间就不会存在锁竞争,从而能够无效的进步并发拜访效率,这就是ConcurrentHashMap所应用的锁分段技术,首先将数据分成一段一段的存储,而后给每一段数据配一把锁,当一个线程占用锁拜访其中一个段数据的时候,其余段的数据也能被其余线程拜访。 另外,ConcurrentHashMap能够做到读取数据不加锁,并且其外部的构造能够让其在进行写操作的时候可能将锁的粒度放弃地尽量地小,不必对整个ConcurrentHashMap加锁。

ConcurrentHashMap是由Segment数组构造和HashEntry数组构造组成。Segment是一种可重入锁ReentrantLock,在ConcurrentHashMap里表演锁的角色,HashEntry则用于存储键值对数据。一个ConcurrentHashMap里蕴含一个Segment数组,Segment的构造和HashMap相似,是一种数组和链表构造, 一个Segment里蕴含一个HashEntry数组,每个HashEntry是一个链表构造的元素, 每个Segment守护着一个HashEntry数组里的元素,当对HashEntry数组的数据进行批改时,必须首先取得它对应的Segment锁。

ConcurrentHashMap为了进步自身的并发能力,在外部采纳了一个叫做Segment的构造,一个Segment其实就是一个类HashTable的构造,Segment外部保护了一个链表数组。

具体理解还是看看其余文章吧,我只是提出有这个货色能够实现并发汇合。

ConcurrentHashMap与其余类的区别

与HashMap的区别是什么?

ConcurrentHashMap是HashMap的升级版,HashMap是线程不平安的,而ConcurrentHashMap是线程平安。而其余性能和实现原理和HashMap相似。

与Hashtable的区别是什么?

Hashtable也是线程平安的,但每次要锁住整个构造,并发性低。相比之下,ConcurrentHashMap获取size时才锁整个对象。

Hashtable对get/put/remove都应用了同步操作。ConcurrentHashMap只对put/remove同步。

Hashtable是疾速失败的,遍历时扭转构造会报错ConcurrentModificationException。ConcurrentHashMap是平安失败,容许并发检索和更新。

而后,在腾讯云社区,我还看到了一个区别,JDK8的ConcurrentHashMap和JDK7的ConcurrentHashMap的区别。

JDK8的ConcurrentHashMap和JDK7的ConcurrentHashMap有什么区别?

其余个性

ConcurrentHashMap是如何保障并发平安的?

JDK7中ConcurrentHashMap是通过ReentrantLock+CAS+分段思维来保障的并发平安的,ConcurrentHashMap的put办法会通过CAS的形式,把一个Segment对象存到Segment数组中,一个Segment外部存在一个HashEntry数组,相当于分段的HashMap,Segment继承了ReentrantLock,每段put开始会加锁。

在JDK7的ConcurrentHashMap中,首先有一个Segment数组,存的是Segment对象,Segment相当于一个小HashMap,Segment外部有一个HashEntry的数组,也有扩容的阈值,同时Segment继承了ReentrantLock类,同时在Segment中还提供了put,get等办法,比方Segment的put办法在一开始就会去加锁,加到锁之后才会把key,value存到Segment中去,而后开释锁。同时在ConcurrentHashMap的put办法中,会通过CAS的形式把一个Segment对象存到Segment数组的某个地位中。同时因为一个Segment外部存在一个HashEntry数组,所以和HashMap比照来看,相当于分段了,每段外面是一个小的HashMap,每段专用一把锁,同时在ConcurrentHashMap的构造方法中是能够设置分段的数量的,叫做并发级别concurrencyLevel.

JDK8中ConcurrentHashMap是通过synchronized+cas来实现了。在JDK8中只有一个数组,就是Node数组,Node就是key,value,hashcode封装进去的对象,和HashMap中的Entry一样,在JDK8中通过对Node数组的某个index地位的元素进行同步,达到该index地位的并发平安。同时外部也利用了CAS对数组的某个地位进行并发平安的赋值。

JDK8中的ConcurrentHashMap为什么应用synchronized来进行加锁?

JDK8中应用synchronized加锁时,是对链表头结点和红黑树根结点来加锁的,而ConcurrentHashMap会保障,数组中某个地位的元素肯定是链表的头结点或红黑树的根结点,所以JDK8中的ConcurrentHashMap在对某个桶进行并发安全控制时,只须要应用synchronized对以后那个地位的数组上的元素进行加锁即可,对于每个桶,只有获取到了第一个元素上的锁,能力操作这个桶,不论这个桶是一个链表还是红黑树。

想比于JDK7中应用ReentrantLock来加锁,因为JDK7中应用了分段锁,所以对于一个ConcurrentHashMap对象而言,分了几段就得有几个ReentrantLock对象,示意得有对应的几把锁。

而JDK8中应用synchronized关键字来加锁就会更节俭内存,并且jdk也曾经对synchronized的底层工作机制进行了优化,效率更好。

JDK7中的ConcurrentHashMap是如何扩容的?

JDK7中的ConcurrentHashMap和JDK7的HashMap的扩容是不太一样的,首先JDK7中也是反对多线程扩容的,起因是,JDK7中的ConcurrentHashMap分段了,每一段叫做Segment对象,每个Segment对象相当于一个HashMap,分段之后,对于ConcurrentHashMap而言,能同时反对多个线程进行操作,前提是这些操作的是不同的Segment,而ConcurrentHashMap中的扩容是仅限于本Segment,也就是对应的小型HashMap进行扩容,所以是能够多线程扩容的。

每个Segment外部的扩容逻辑和HashMap中一样。

JDK8中的ConcurrentHashMap是如何扩容的?

首先,JDK8中是反对多线程扩容的,JDK8中的ConcurrentHashMap不再是分段,或者能够了解为每个桶为一段,在须要扩容时,首先会生成一个双倍大小的数组,生成完数组后,线程就会开始转移元素,在扩容的过程中,如果有其余线程在put,那么这个put线程会帮忙去进行元素的转移,尽管叫转移,然而其实是基于原数组上的Node信息去生成一个新的Node的,也就是原数组上的Node不会隐没,因为在扩容的过程中,如果有其余线程在get也是能够的。

JDK8中的ConcurrentHashMap是如何扩容的?

CounterCell是JDK8中用来统计ConcurrentHashMap中所有元素个数的,在统计ConcurentHashMap时,不能间接对ConcurrentHashMap对象进行加锁而后再去统计,因为这样会影响ConcurrentHashMap的put等操作的效率,在JDK8的实现中应用了CounterCell+baseCount来辅助进行统计,baseCount是ConcurrentHashMap中的一个属性,某个线程在调用ConcurrentHashMap对象的put操作时,会先通过CAS去批改baseCount的值,如果CAS批改胜利,就计数胜利,如果CAS批改失败,则会从CounterCell数组中随机选出一个CounterCell对象,而后利用CAS去批改CounterCell对象中的值,因为存在CounterCell数组,所以,当某个线程想要计数时,先尝试通过CAS去批改baseCount的值,如果没有批改胜利,则从CounterCell数组中随机取出来一个CounterCell对象进行CAS计数,这样在计数时进步了效率。

所以ConcurrentHashMap在统计元素个数时,就是baseCount加上所有CountCeller中的value值,所得的和就是所有的元素个数。

注:其余个性局部来自于腾讯云+社区