HashMap和Hashtable的区别什么是HashMapHashMap是JDK1.2引入的Map的实现类。HashMap是基于哈希表实现的。每个元素都是一个键值对。它使用单链表来解决冲突。当容量不足时(超过阈值),也会自动增加。其次,HashMap不是线程安全的,只能在单线程环境下使用。在多线程环境下,可以使用并发契约下的并发HashMap。不懂线程安全的可以看我的文章:Java并发编程多线程HashMap实现了Serializable接口,所以支持序列化,实现了Cloneable接口,可以克隆。HashMap中的key和value都允许为null。key为null的键值对总是放在以table[0]为头节点的链表中。什么是哈希表?Hashtable也是基于哈希表实现的。同样,每个元素都是一个键值对。在内部,单链表用于解决冲突。当容量不足时(超过阈值),它也会自动增长。Hashtable也是JDK1.0引入的一个类,它是线程安全的,可以在多线程环境下使用。Hashtable还实现了Serializable接口,支持序列化,实现了Cloneable接口,可以被克隆。也就是说,这两件事大部分是相同的。Hashtable和HashMap的区别首先从上面可以得出线程安全性不同。HashMap线程不安全,HashTable线程安全。contained的contains方法不同,HashMap没有contains方法。Hashmap允许key和value为空。哈希值的计算方式不同。.不同的扩展方法。有不同的方法来解决哈希冲突。HashMap继承自AbstractMap类,Hashtable继承自Dictionary类。对外提供的接口不同。Hashtable比HashMap多了两个方法elements()和contains()。如果需要详细了解区别,可以去浏览器获取详细的区别和原理。ConcurrentHashMap的作用看下面我箭头所指的地方。因为HashMap是线程不安全的,Hashtable虽然是线程安全的,但是是一个废弃的类。既然淘汰了,我们基本不用了。那么什么可以代替Hashtable成为HashMap的线程安全类呢?concurrentHashMap可以在并发环境下使用,支持线程安全。线程不安全的HashMap和线程不安全的HashMap促使了JDK5中ConcurrentHashMap的诞生。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也是线程安全的,但是每次都需要对整个结构加锁,并发度低。相比之下,ConcurrentHashMap只有在获取大小时才会锁定整个对象。哈希表使用同步操作来获取/放置/删除。ConcurrentHashMap只同步put/remove。Hashtable属于快速失败,遍历时改变结构会报错ConcurrentModificationException。ConcurrentHashMap是故障安全的,允许并发检索和更新。然后,在腾讯云社区,我也看到了一个区别,JDK8的ConcurrentHashMap和JDK7的ConcurrentHashMap的区别。JDK8的ConcurrentHashMap和JDK7的ConcurrentHashMap有什么区别?其他特性ConcurrentHashMap是如何保证并发安全的?JDK7中的ConcurrentHashMap是通过ReentrantLock+CAS+segmentation来保证并发安全的。ConcurrentHashMap的put方法会通过CAS在Segment数组中存储一个Segment对象。Segment内部有一个HashEntry数组,相当于分段。HashMap、Segment继承ReentrantLock,每次put一开始都会加锁。在JDK7的ConcurrentHashMap中,首先有一个Segment数组,里面存放的是Segment对象。Segment相当于一个小的HashMap。Segment内部有一个HashEntry数组,也有一个扩容的门槛。同时,Segment继承了ReentrantLock类。提供put、get等方法。比如Segment的put方法会在开头加锁。加锁后,key和value会存放在Segment中,然后释放锁。同时在ConcurrentHashMap的put方法中,会通过CAS在Segment数组的某个位置存储一个Segment对象。同时,由于Segment内部有一个HashEntry数组,与HashMap相比,相当于一个Segment。每个段都是一个小的HashMap,每个段共享一把锁。同时可以在ConcurrentHashMap的构造方法中设置。段数称为并发级别concurrencyLevel。JDK8中的ConcurrentHashMap是通过synchronized+cas实现的。JDK8中只有一个数组,就是Node数组。Node是由key、value、hashcode封装的对象。与HashMap中的Entry相同。在JDK8中,同步Node数组某个索引位置的元素到达索引。位置的并发安全。同时内部也使用CAS对数组的某个位置进行并发安全赋值。为什么JDK8中的ConcurrentHashMap要用synchronized来加锁?在JDK8中使用synchronized锁时,链表的头节点和红黑树的根节点是被锁住的,而ConcurrentHashMap保证数组中某个位置的元素一定是链表的头节点或者红黑树的根节点。根节点,所以JDK8中的ConcurrentHashMap在对某个bucket进行并发安全控制时,只需要使用synchronized将数组上的元素锁定在当前位置即可。对于每一个桶,只有第一个元素上的锁才能操作这个桶,不管这个桶是链表还是红黑树。想着在JDK7中使用ReentrantLock来锁定。因为在JDK7中使用了分段锁,对于一个ConcurrentHashMap对象来说,必须有几个ReentrantLock对象分成几个段,说明必须有对应的锁。在JDK8中,使用synchronized关键字加锁会节省更多的内存,而且jdk还优化了synchronized的底层工作机制,效率更高。ConcurrentHashMap在JDK7中是如何扩展的?JDK7中的ConcurrentHashMap和JDK7中的HashMap的扩展是不一样的。首先,JDK7还支持多线程扩展。原因是JDK7中的ConcurrentHashMap是分段的。每个Segment称为一个Segment对象,每个Segment对象就相当于一个HashMap被切分后,对于ConcurrentHashMap来说,可以支持多个线程同时操作,前提是这些操作是在不同的Segment上进行的,扩展在ConcurrentHashMap仅限于这个Segment,也就是对应的小HashMap的扩展。所以可以多线程扩展。每个段内部的扩展逻辑与HashMap相同。ConcurrentHashMap在JDK8中是如何扩展的?首先,JDK8支持多线程扩展。JDK8中的ConcurrentHashMap不再分段,或者可以理解为每个bucket是一个段。当需要扩容时,会先生成一个双倍大小的数组。数组生成后,线程会开始传递元素。在扩容的过程中,如果put中还有其他线程,那么这个put线程会帮忙传递元素。虽然叫transfer,但实际上是根据原数组上的Node信息生成一个新的Node,即原数组上的Node不会消失,因为在扩容的过程中,如果有其他线程在get中,也是可以的。ConcurrentHashMap在JDK8中是如何扩展的?JDK8中使用CounterCell来统计ConcurrentHashMap中所有元素的个数。在统计ConcurrentHashMap的时候,不能直接锁住ConcurrentHashMap对象再统计,因为这样会影响ConcurrentHashMapput等操作的效率。在JDK8的实现中使用了CounterCell+baseCount来辅助统计。baseCount是ConcurrentHashMap中的一个属性。当一个线程调用ConcurrentHashMap对象的put操作时,会先通过CAS修改baseCount的值。如果CAS修改成功,则计数成功。如果CAS修改失败,则会从CounterCell数组中随机选择一个CounterCell对象,然后使用CAS修改CounterCell对象中的值。因为有一个CounterCell数组,当一个线程要计数的时候,先尝试通过CAS修改,如果baseCount的值没有修改成功,就从CounterCell数组中随机取出一个CounterCell对象进行CAS计数,提高了效率数数。所以ConcurrentHashMap在统计元素个数的时候,就是baseCount加上所有CountCellers中的值,得到的和就是所有元素的个数。注:其他功能部分来自腾讯云+社区
