主要內容:
- Hashtable與HashMap比較
- 繼承關系、關鍵屬性、構造函數
- 插入、查找元素
- 擴容
Hashtable概述
一般提到Hashtable會將它與HashMap進行比較,下面先簡要說下兩者的聯系。
-
相同點:基于哈希表的Map接口的實現,存儲的是鍵值對。
以Key-Value鍵值對的形式存儲數據。 -
不同點:Hashtable繼承了
Dictionary
,而HashMap繼承了AbstarctMap
;Hashtable不允許key、value值為null,而HashMap允許key、value值為null;Hashtable線程安全,HashMap線程安全;擴容時Hashtable數組擴大一倍+1,HashMap擴大一倍。
源碼分析
繼承關系
Hashtable繼承關系.png
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable
- 繼承Dictionary抽象類,定義了對鍵值對的操作
關鍵屬性
//Entry類型的數組,以鍵值對形式存儲
private transient Entry<K,V>[] table;
//實際元素的數量
private transient int count;
//下次擴容的臨界值,size>=threshold時會進行擴容,threshold=capacity * loadFactor
private int threshold;
//加載因子
private float loadFactor;
//被修改的次數,實現fail fast機制
private transient int modCount = 0;
構造函數
//使用指定的容量大小以及加載因子構造Hashtable,初始化數組
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;
this.loadFactor = loadFactor;
table = new Entry[initialCapacity];
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
initHashSeedAsNeeded(initialCapacity);
}
//使用指定容量大小和默認加載因子0.75構造Hashtable
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
//使用默認初始容量大小11和默認加載因子0.75構造Hashtable
public Hashtable() {
this(11, 0.75f);
}
//構造一個指定map的HashMap,使用默認加載因子0.75
public Hashtable(Map<? extends K, ? extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}
插入
方法是同步的。
public synchronized V put(K key, V value) {
//key為null,拋出異常
if (value == null) {
throw new NullPointerException();
}
//Hashtable已存在對應key的鍵值對,新值替換舊值
Entry tab[] = table;
int hash = hash(key);
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
V old = e.value;
e.value = value;
return old;
}
}
//Hashtable不存在對應key的鍵值對
modCount++;
if (count >= threshold) {//若實際容量>閾值
rehash();//擴容
tab = table;
hash = hash(key);
index = (hash & 0x7FFFFFFF) % tab.length;
}
//創建新的節點
Entry<K,V> e = tab[index];
tab[index] = new Entry<>(hash, key, value, e);//創建新節點,插入到Hashtable數組的index處,下一個元素指向e
count++;
return null;
}
查找
方法同步,根據鍵值key查找對應的值。
public synchronized V get(Object key) {
Entry tab[] = table;
int hash = hash(key);
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return e.value;
}
}
return null;
}
擴容
當實際元素占分配容量的75%時進行擴容。數組大小擴大一倍+1,然后重新計算每個元素在數組中的位置。
protected void rehash() {
int oldCapacity = table.length;
Entry<K,V>[] oldMap = table;
// overflow-conscious code
int newCapacity = (oldCapacity << 1) + 1;
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
// Keep running with MAX_ARRAY_SIZE buckets
return;
newCapacity = MAX_ARRAY_SIZE;
}
Entry<K,V>[] newMap = new Entry[newCapacity];
modCount++;
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
boolean rehash = initHashSeedAsNeeded(newCapacity);
table = newMap;
for (int i = oldCapacity ; i-- > 0 ;) {
for (Entry<K,V> old = oldMap[i] ; old != null ; ) {//遍歷舊的Hashtable
Entry<K,V> e = old;
old = old.next;
if (rehash) {
e.hash = hash(e.key);
}
int index = (e.hash & 0x7FFFFFFF) % newCapacity;//重新計算槽位index
e.next = newMap[index];
newMap[index] = e;
}
}
}