關(guān)于HashTable

前言

面試官問ConcurrentHashMap和HashTable的區(qū)別谋国,當(dāng)時(shí)很緊張前痘,就回答了HashTable的方法都加上了synchronized關(guān)鍵字苛萎,吞吐量沒有ConcurrentHashMap高洞翩,現(xiàn)在悔的腸子都青了焊虏。

1.增加元素 put方法

    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;
    }

當(dāng)放進(jìn)的value等于null的時(shí)候港令,就拋出空指針異常啥容。這就是“著名的”HashTable不可以放null指針锈颗,而HashMap可以把null當(dāng)成value的論述。
如果要放入的桶tab[index]這個(gè)坑位上已經(jīng)有人占了咪惠,就先遍歷一遍击吱,如果存在哈希沖突,那么就把舊值換成新值遥昧。
如果沒有tab[index]這個(gè)坑位沒有被占用覆醇,或者說沒有發(fā)生哈希沖突,那么就調(diào)用addEntry方法炭臭。

1.1 肩負(fù)重任的addEntry方法

    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++;
    }

第一步永脓,如果HashTable中的元素?cái)?shù)量大于閥值的話,就擴(kuò)容徽缚,并重新定位index憨奸。
第二步,與HashMap不同凿试,HashTable會把新放入的節(jié)點(diǎn)作為鏈表頭部排宰。

1.2 擴(kuò)容

    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;
            }
        }
    }

第一步,確認(rèn)新的桶容量newCapacity那婉,是原來的二倍+1
第二部板甘,將原來桶的頭節(jié)點(diǎn)賦值到新的桶中,當(dāng)然了详炬,要重新計(jì)算index盐类,計(jì)算index的方法與put一樣。
由于沒有使用紅黑樹這一數(shù)據(jù)結(jié)構(gòu)呛谜,擴(kuò)容方法顯的十分簡單在跳。

2.查詢 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;
            }
        }
        return null;
    }

定位index,然后遍歷鏈表隐岛,找到返回相應(yīng)的值猫妙,找不到就返回null。
get方法的思路和HashMap基本方法是比較一致的

3.刪除元素 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) {
                    prev.next = e.next;
                } else {
                    tab[index] = e.next;
                }
                count--;
                V oldValue = e.value;
                e.value = null;
                return oldValue;
            }
        }
        return null;
    }

確認(rèn)了對應(yīng)的位置之后聚凹,就按照在鏈表中刪除節(jié)點(diǎn)的辦法來刪除該節(jié)點(diǎn)割坠。
如果是頭結(jié)點(diǎn)的話,prev就是null妒牙,只需更換頭結(jié)點(diǎn)就好tab[index] = e.next;
如果不是頭結(jié)點(diǎn)彼哼,這個(gè)時(shí)候prev已經(jīng)不是null了,prev的next指針指向要?jiǎng)h除節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)就好(有可能是null)
如果沒有對應(yīng)key要?jiǎng)h除的節(jié)點(diǎn)湘今,也沒關(guān)系敢朱,返回null就好。
在這里,也不用考慮TreeNode帶來的影響蔫饰。

結(jié)尾

雖然HashTable給方法都加上了synchronized關(guān)鍵字修飾琅豆,但是在組合使用HashTable的api的時(shí)候,還是有可能出現(xiàn)異常的篓吁,具體可以參考《Java并發(fā)編程實(shí)戰(zhàn)》
面試無處不留坑茫因,平時(shí)真的要多寫多看多總結(jié)!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末杖剪,一起剝皮案震驚了整個(gè)濱河市冻押,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌盛嘿,老刑警劉巖洛巢,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異次兆,居然都是意外死亡稿茉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進(jìn)店門芥炭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漓库,“玉大人,你說我怎么就攤上這事园蝠∶燧铮” “怎么了?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵彪薛,是天一觀的道長茂装。 經(jīng)常有香客問我,道長善延,這世上最難降的妖魔是什么少态? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮易遣,結(jié)果婚禮上况增,老公的妹妹穿的比我還像新娘。我一直安慰自己训挡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布歧强。 她就那樣靜靜地躺著澜薄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪摊册。 梳的紋絲不亂的頭發(fā)上肤京,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼忘分。 笑死棋枕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的妒峦。 我是一名探鬼主播重斑,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼肯骇!你這毒婦竟也來了窥浪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤笛丙,失蹤者是張志新(化名)和其女友劉穎漾脂,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胚鸯,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡骨稿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了姜钳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坦冠。...
    茶點(diǎn)故事閱讀 40,567評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖傲须,靈堂內(nèi)的尸體忽然破棺而出蓝牲,到底是詐尸還是另有隱情,我是刑警寧澤泰讽,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布例衍,位于F島的核電站,受9級特大地震影響已卸,放射性物質(zhì)發(fā)生泄漏佛玄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一累澡、第九天 我趴在偏房一處隱蔽的房頂上張望梦抢。 院中可真熱鬧,春花似錦愧哟、人聲如沸奥吩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽霞赫。三九已至,卻和暖如春肥矢,著一層夾襖步出監(jiān)牢的瞬間端衰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留旅东,地道東北人灭抑。 一個(gè)月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像抵代,于是被迫代替她去往敵國和親腾节。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評論 2 359

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