文章目录
(一)HashTable子父层级:
HashTable继承于抽象类Dictionary,主要是定义一些抽象方法,用于HashTable子类实现。
(二)Map实现类
1、HashTable实现类
(1)HashTable底层是Entry数组+链表,transient Entry<?,?>[] table;
(2)HashTable默认容量为11或者构造方法指定初始容量,无参构造器中默认容量11和负载因子0.75f;
(3)HashTable扩容是翻倍+1,int newCapacity = (oldCapacity << 1) + 1;//(位运算:左移一位)
(4)HashTable利用无参和有参构造方法可以指定负载因子,默认为11和0.75f;
(5)HashTable重要成员变量:扩容阈值int threshold(默认11*0.75f)、负载因子float loadFactor(默认0.75,可设置),
存储数组为Entry<?,?>[] table;
(6)HashTable返回size方法中使用的count成员,HashMap的size方法返回的是size成员;
(7)HashTable是线程安全的,因为其底层涉及到数据的方法均带有synchronized保证在多线程环境中数据安全,但因此性能也较低。
2、常见源码
(1)构造方法
//无参构造,使用默认容量11和默认负载因子0.75f
public Hashtable() {
this(11, 0.75f);
}
//带容量设置的含参构造方法,默认0.75f负载因子
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
//基于map映射到新的集合
public Hashtable(Map<? extends K, ? extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}
//指定初始容量和负载因子
public Hashtable(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)//防止数组越界
throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
if (loadFactor <= 0 || Float.isNaN(loadFactor))//判断参数是否不是正数和负载因子是否为浮点型
throw new IllegalArgumentException("Illegal Load: "+loadFactor);
if (initialCapacity==0)
initialCapacity = 1;//强行改变初始值为0的设置
this.loadFactor = loadFactor;//设置负载因子
table = new Entry<?,?>[initialCapacity];//对存储数组进行初始化
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);//计算阈值并转为整型
}
(2)put方法
//涉及到数据,因此该方法有synchronized同步且K——V键值对都不允许为null
public synchronized V put(K key, V value) {
if (value == null) {//不允许值为null
throw new NullPointerException();
}
Entry<?,?> tab[] = table;
int hash = key.hashCode();//与hashMap区别很大,此处直接使用key的hashCode
int index = (hash & 0x7FFFFFFF) % tab.length;//计算key在数组中的桶位置
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {//遍历该桶,是否已存在对应的key
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;//旧值
entry.value = value;//设置新的值
return old;//返回旧值
}
}
//将新的键值对保存在数组中
addEntry(hash, key, value, index);
return null;
}
//键值对入数组或者链表
private void addEntry(int hash, K key, V value, int index) {
modCount++;
Entry<?,?> tab[] = table;
if (count >= threshold) {//是否扩容
rehash();//扩容
tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;//利用模运算进行取余进行确定桶位置
}
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e);//赋值
count++;//数量自增
}
(3)rehash扩容方法
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
int newCapacity = (oldCapacity << 1) + 1;//扩容值即左移一位并+1
if (newCapacity - MAX_ARRAY_SIZE > 0) {//防止超过最大整型值
if (oldCapacity == MAX_ARRAY_SIZE)
return;
newCapacity = MAX_ARRAY_SIZE;
}
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];//初始化最新数组
modCount++;
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);//新的扩容阈值
table = newMap;
for (int i = oldCapacity ; i-- > 0 ;) {//从后往前遍历数组桶
for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {//对桶链表进行遍历
Entry<K,V> e = old;
old = old.next;//递推旧桶位置
int index = (e.hash & 0x7FFFFFFF) % newCapacity;//确定新桶位置
e.next = (Entry<K,V>)newMap[index];//新链表链接,此处为头插法
newMap[index] = e;//重新赋值给桶位置
}
}
}
(4)get方法
public synchronized V get(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;//取模运算确定桶位置
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {//遍历链表
if ((e.hash == hash) && e.key.equals(key)) {
return (V)e.value;//返回指定key对应的值
}
}
return null;
}
(5)remove方法
public synchronized V remove(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;//确定桶位置
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>)tab[index];
for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {//遍历链表
if ((e.hash == hash) && e.key.equals(key)) {
modCount++;
if (prev != null) {//删除节点前是否有Entry键值对
prev.next = e.next;//对键值对进行删除
} else {
tab[index] = e.next;//删除节点为桶位置即直接将后续键值对提前
}
count--;
V oldValue = e.value;
e.value = null;
return oldValue;//返回删除值
}
}
return null;
}
3、总结
(1)HashTable底层为Entry数组+链表,涉及数据的方法如put、remove、get等方法
均有synchronized保证线程安全和同步即数据强一致性,相对来说性能将下降;
(2)HashTable核心成员变量Entry<K,V> table,int count,阈值int threshold,负载因子float loadFactor等,
无参和有参构造函数对容量和负载因子赋值,无参则默认11和0.75f,扩容则是当前容量翻倍并+1进行数组扩容;
(3)HashTable获取size是由成员变量count变量获取,HashMap则是使用成员变量size获取,
扩容机制不同即前者使用翻倍+1,后者使用翻倍,hash也不一样即前者直接使用key的hashcode,后者使用扰乱算法。