1. HashTable介绍
从下面源码中的结构图可以看出,HashTable继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口。HashTable是通过数组加链表的方式存储数据。
和HashMap一样,Hashtable但是Hashtable是一个散列表,他存储的内容是键值对(key-value)映射。但是区别于HashMap,Hashtable是一个线程安全的容器,而且Hashtable的key、value都不可以为null。
1.1 HashTable的底层数据结构
Hashtable是通过使用“拉链法”实现的哈希表,它包含几个重要的成员变量:table,count,threshold,loadFactor,modCount,如下图所示:
其中table是一个Entry[]数组类型,Entry实际上就是一个单向链表,哈希表中的“key-value”键值对都是存储再Entry数组中,对于Hashtable中的get和put是通过拉链法解决哈希冲突的,如下图所示,对于哈希码相同的元素采取一个链表的方式存储元素。
count是Hashtable的大小,表示Hashtable保存的键值对的数量,对于java源码中的put方法采用addEntry中对count进行自增操作,记录Hashtable的键值对数量。
threshold是Hashtable的阈值,用于判断是否需要调整Hashtable的容量。threshold的值=“容量*加载因子”。
loadFactor就是加载因子,Hashtable的有参构造函数有两个,第一个构造函数有两个参数,第一个是输入的初始化容量,第二个是加载因子。Entry数组的容量为输入的容量参数。而Hashtable的容量是initialCapacity * loadFactor和Integer.MAX_VALUE - 7的较小值。对于一个参数构造器是默认加载因子为0.75。
modCount是用来实现fail-fast机制的。modCount指的是修改次数,对Hashtable内容的修改都将增加这个值,在迭代过程中判断modCount是否于expectedCount相等,如果不相等就代表有其他线程修改了Map,这就是fail-fast机制。
2. HashMap的介绍
HashMap是一个非线程安全的容器,HashMap继承AbstractMap<K,V>抽象类,实现Map<K,V>和Serializable接口。HashMap也是通过使用拉链法解决哈希冲突。
2.1 HashMap的底层数据结构
在jdk1.8之前HashMap是由数组+链表组成,数组存储不同的哈希码的数据,但是如果发生hash冲突,那就使用链表存储发生哈希冲突的数据。在jdk1.8之后,当链表的和长度大于阈值(默认为8)时将链表转化为红黑树,但是如果数组长度小于64,那会先对数组进行扩容,解决哈希冲突。
2.2 HashMap和HashTable的区别
HashMap是一个非线程安全的容器,HashTable是一个线程安全的容器。
HashMap的底层数据结构是数组+链表/红黑树,而HashTable的底层数据结构是数组+链表
HashMap可以存储null的key和value,但是null作为键只有一个,null作为值可以有多个。Hashtable不允许有null键和null值。
3. ConcurrentHashMap的介绍
ConcurrentHashMap是一个线程安全的Map,底层存储是通过Node数组+链表/红黑树进行存储的。下图是ConcurrentHashMap的继承结构。ConcurrentHashMap通过使用cas加锁的机制实现了线程安全。
3.1 ConcurrentHashMap和HashTable的区别
ConcurrentHashMap的线程安全是通过Synchronized 锁加 CAS 的机制实现的,而HashTable是通过Synchronized关键字实现。
两者的底层存储也不一样,ConcurrentHashMap是通过Node数组+链表/红黑树,HashTable是通过数组加链表实现的。