問:談談你對 Hashtable 原理的理解?
答:Hashtable 與 HashMap 類似,也是一個存儲鍵值對的散列表,Hashtable 繼承自 Dictionary 類,實現了 Map、Cloneable、Serializable 接口,Hashtable 是通過拉鏈法實現哈希表的,其構造方法主要有四個,如下:
//默認構造方法,容量為 11,加載因子為 0.75
public Hashtable()
//通過指定初始容量與默認加載因子 (0.75) 構造一個新的空哈希表
public Hashtable( int initialCapacity )
// 構造一個給定 Map 的新哈希表
public Hashtable(Map < ? extends K , ? extends V > t )
// 通過指定初始容量和指定加載因子構造一個新的空哈希表
public Hashtable( int initialCapacity, float loadFactor )
- Hashtable 的并發安全 put 原理是先判斷 value 是否為空,為空則拋出異常,然后計算 key 的 hash 值(key.hashCode()),如果 key 為空則拋出異常,否則根據 hash 值獲得 key 在 table 數組中的位置 index,接著如果 table[index] 元素不為空,則進行迭代,如果遇到相同的 key,則直接替換,并返回舊 value,否則,我們可以將其插入到 table[index] 位置(發生哈希碰撞時變為鏈表頭,否則該數組位置單個元素);整體看來和 HashMap 十分相似,算是精簡版的 HashMap put 原理,具體源碼分析如下:
//put方法是通過synchronized保證單操作并發安全的
public synchronized V put(K key, V value) {
// Hashtable的value不允許為空!!!
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
HashtableEntry tab[] = table;
//hash方法的實質是key.hashCode(),所以Hashtable的key也不允許為空!!!
int hash = hash(key);
// 依據key的hash值計算出index值來確定其在table[]中的位置
int index = (hash & 0x7FFFFFFF) % tab.length;
// 迭代index索引位置的鏈表,如果該位置處的鏈表存在相同的key,
// 則替換value,返回舊的value
for (HashtableEntry<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;
}
}
modCount++;
if (count >= threshold) {
//如果超過閥值,就進行rehash操作
rehash();
tab = table;
hash = hash(key);
index = (hash & 0x7FFFFFFF) % tab.length;
}
//插入后返回的為null
HashtableEntry<K, V> e = tab[index];
//創建新的 Entry 節點,并將新的 Entry 插入 Hashtable 的 index 位置,
// 并設置 e 為新的 Entry 的下一個元素
tab[index] = new HashtableEntry<>(hash, key, value, e);
count++;
return null;
}
private static int hash(Object k) {
return k.hashCode();
}
- Hashtable 的并發安全 get 方法原理是先通過 hash()方法求得 key 的哈希值,然后根據 hash 值得到 index 索引,然后迭代鏈表,返回匹配的 key 對應的 value,如果找不到則返回 null,源碼如下:
public synchronized V get (Object key ){
HashtableEntry tab[] = table;
int hash = hash(key);
int index = (hash & 0x7FFFFFFF) % tab.length;
for (HashtableEntry<K, V> e = tab[index]; e != null; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return e.value;
}
}
return null;
}
問:說說為什么 Hashtable 的 key、value 都不允許為 null,而類似實現的 HashMap 就可以?
答:因為 Hashtable 比 HashMap 先出來,且 Hashtable 繼承自 Dictionary 類,Dictionary 抽象層的 put 方法定義是不允許 key、value 為空的,也就是說設計者當初就沒考慮 null 情況,而后來 HashMap 卻考量了這種情況;此外由于 Hashtable 在 put 實現時沒有對 key 進行判斷,獲取 key 的哈希值是直接通過 key.hashCode() 方法獲取的,所以 key 如果為空就崩潰了,而 value 是直接防御性實現的,而對于 HashMap 來說對于 key、value 為空都進行了單獨處理。