Hashtable 源碼解析

前言

注意是 Hashtable 不是 HashTable (t為小寫)虏束,這不是違背了駝峰定理了嘛灯抛?這還得從 Hashtable 的出生說(shuō)起随橘,
Hashtable 是在Java1.0的時(shí)候創(chuàng)建的蕴坪,而集合的統(tǒng)一規(guī)范命名是在后來(lái)的Java2開始約定的还栓,而當(dāng)時(shí)又發(fā)布了新的集合代替它碌廓,所以這個(gè)命名也一直使用到現(xiàn)在,
所以 Hashtable 是一個(gè)過(guò)時(shí)的集合了蝙云,不推崇大家使用這個(gè)類氓皱,雖說(shuō) Hashtable 是過(guò)時(shí)的了路召,我們還是有必要分析一下它勃刨,以便對(duì)Java集合框架有一個(gè)整體的認(rèn)知。
HashTable 比較古老股淡, 是JDK1.0就引入的類身隐,而HashMap 是 1.2 引進(jìn)的 Map 的一個(gè)實(shí)現(xiàn)。
HashTable 是線程安全的唯灵,能用于多線程環(huán)境中贾铝。
Hashtable 同樣也實(shí)現(xiàn)了 Serializable 接口,支持序列化埠帕,也實(shí)現(xiàn)了 Cloneable 接口垢揩,能被克隆。
成員變量

//為什么要用int最大值-8敛瓷,官方解釋:有些虛擬機(jī)在數(shù)組中保留了一些頭信息叁巨。避免內(nèi)存溢出!
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//table是一個(gè)Entry[]數(shù)組類型呐籽,而Entry實(shí)際上就是一個(gè)單向鏈表锋勺。哈希表的"key-value鍵值對(duì)"都是存儲(chǔ)在Entry數(shù)組中的蚀瘸。
private transient Entry[] table;
// Hashtable中元素的實(shí)際數(shù)量
private transient int count;
// 閾值,用于判斷是否需要調(diào)整Hashtable的容量(容量*加載因子 >= threshold 是需要調(diào)整容量)
private int threshold;
// 加載因子 默認(rèn)0.75f
private float loadFactor;
// Hashtable被改變的次數(shù) 是Hashtable被修改或者刪除的次數(shù)總數(shù)庶橱。用來(lái)實(shí)現(xiàn)“fail-fast”機(jī)制的
private transient int modCount = 0;

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);
//初始容量最小為1
if (initialCapacity==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry<?,?>[initialCapacity];
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}
PUT方法

線程安全
key value 都不能為null
hash & 0x7FFFFFFF 是為了避免負(fù)數(shù)贮勃,為什么不用abs(絕對(duì)值)?涉及一個(gè)int負(fù)數(shù)最大值問(wèn)題
如果key已存在會(huì)返回老的value
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}

addEntry(hash, key, value, index);
return null;

}
addEntry方法

如果hashtable 當(dāng)前數(shù)量>=閾(愈)值苏章,需要擴(kuò)容
鏈表疊加
private void addEntry(int hash, K key, V value, int index) {
modCount++;

Entry<?,?> tab[] = table;
if (count >= threshold) {
    // Rehash the table if the threshold is exceeded
    rehash();

    tab = table;
    hash = key.hashCode();
    index = (hash & 0x7FFFFFFF) % tab.length;
}

// Creates the new entry.
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;

}
rehash方法

容量擴(kuò)大兩倍+1
需要將原來(lái)HashTable中的元素一一復(fù)制到新的HashTable中寂嘉,這個(gè)過(guò)程是比較消耗時(shí)間的
鏈表順序變了
@SuppressWarnings("unchecked")
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] 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<?,?>[] 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;
    }
}

}
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];
//遍歷Entry鏈表,e為當(dāng)前節(jié)點(diǎn)布近,prev為上一個(gè)節(jié)點(diǎn)
for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
//為什么不直接只用 e.key.equals(key) 來(lái)判斷垫释,因?yàn)閑.hash == hash性能更優(yōu)
if ((e.hash == hash) && e.key.equals(key)) {
modCount++;
if (prev != null) {
//如果是鏈表的中間值
prev.next = e.next;
} else {
//如果是鏈表的第一個(gè)
tab[index] = e.next;
}
count--;
V oldValue = e.value;
e.value = null;
return oldValue;
}
}
return null;
}

原文地址:https://zhuanlan.zhihu.com/p/77706764

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市撑瞧,隨后出現(xiàn)的幾起案子棵譬,更是在濱河造成了極大的恐慌,老刑警劉巖预伺,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件订咸,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡酬诀,警方通過(guò)查閱死者的電腦和手機(jī)脏嚷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)瞒御,“玉大人父叙,你說(shuō)我怎么就攤上這事‰热梗” “怎么了趾唱?”我有些...
    開封第一講書人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蜻懦。 經(jīng)常有香客問(wèn)我甜癞,道長(zhǎng),這世上最難降的妖魔是什么宛乃? 我笑而不...
    開封第一講書人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任悠咱,我火速辦了婚禮,結(jié)果婚禮上征炼,老公的妹妹穿的比我還像新娘析既。我一直安慰自己,他們只是感情好谆奥,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開白布眼坏。 她就那樣靜靜地躺著,像睡著了一般雄右。 火紅的嫁衣襯著肌膚如雪空骚。 梳的紋絲不亂的頭發(fā)上纺讲,一...
    開封第一講書人閱讀 51,604評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音囤屹,去河邊找鬼熬甚。 笑死,一個(gè)胖子當(dāng)著我的面吹牛肋坚,可吹牛的內(nèi)容都是我干的乡括。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼智厌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼诲泌!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起铣鹏,我...
    開封第一講書人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤敷扫,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后诚卸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體葵第,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年合溺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了卒密。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡棠赛,死狀恐怖哮奇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情睛约,我是刑警寧澤鼎俘,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站痰腮,受9級(jí)特大地震影響而芥,放射性物質(zhì)發(fā)生泄漏律罢。R本人自食惡果不足惜膀值,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望误辑。 院中可真熱鬧沧踏,春花似錦、人聲如沸巾钉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)砰苍。三九已至潦匈,卻和暖如春阱高,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背茬缩。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工赤惊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人凰锡。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓未舟,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親掂为。 傳聞我的和親對(duì)象是個(gè)殘疾皇子裕膀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容