HashTable解析

HashTable本身和hashMap差距不大,看了幾個(gè)hashTable的內(nèi)部方法實(shí)現(xiàn)榜晦,發(fā)現(xiàn)內(nèi)部方法沒(méi)有上鎖堡赔,但是用public修飾的方法全部用synchronize加上了方法鎖港庄,這樣是極其消耗性能的锰茉,好暇屋,我們來(lái)看幾個(gè)內(nèi)部方法的實(shí)現(xiàn)。

rehash()

    protected void rehash() {
        int oldCapacity = table.length;
        Entry<?,?>[] oldMap = table;

        // 將容量擴(kuò)大為原來(lái)的兩倍+1
        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;
        }
        //新數(shù)組
        Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];

        modCount++;
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        table = newMap;


        //這個(gè)地方有點(diǎn)意思扬霜,跟hashMap和ConcurrentHashmap的實(shí)現(xiàn)不一樣
        //前兩者是使用hash值與舊數(shù)組長(zhǎng)度進(jìn)行與操作,然后分成兩條鏈而涉,直接放到相應(yīng)位置
        //而hashTable是從前面插入著瓶,也就是每插入一個(gè)節(jié)點(diǎn),就將這個(gè)節(jié)點(diǎn)的next指向原有的首節(jié)點(diǎn)
        //然后插入節(jié)點(diǎn)充當(dāng)當(dāng)前位置的首節(jié)點(diǎn)
        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;

                //這個(gè)地方有點(diǎn)疑問(wèn)
                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                e.next = (Entry<K,V>)newMap[index];
                newMap[index] = e;
            }
        }
    }   

在數(shù)據(jù)遷移過(guò)程中啼县,我們可以看到hashTable是通過(guò)取余來(lái)獲取位置的材原,這個(gè)效率肯定不如hashmap,還有一個(gè)地方是hash和0x7fffffff進(jìn)行了與操作季眷,這個(gè)三個(gè)集合都有點(diǎn)不同余蟹,hashmap是先將hash值無(wú)符號(hào)右移16位然后再跟自身進(jìn)行異或操作,這個(gè)是為了減少hash沖突子刮,但是在ConcurrentHashmap中威酒,它的hash值計(jì)算在這個(gè)基礎(chǔ)上還跟0x7fffffff進(jìn)行了與操作窑睁,而hashtable沒(méi)有高16和低16位的異或操作,直接跟0x7fffffff進(jìn)行與操作葵孤。
總結(jié)一下:
1担钮、hashmap跟hashtable和ConcurrentHashmap的區(qū)別在于,后兩者都是線程安全的集合尤仍,然后他們都跟0x7fffffff進(jìn)行了與操作
2箫津、hashTable的位置計(jì)算直接通過(guò)取余的方式,在位置選擇上的性能上會(huì)低于hashmap和concurrentHashmap
3宰啦、ConcurrentHashmap中有個(gè)數(shù)據(jù)遷移的地方比較高明苏遥,在分成兩條鏈進(jìn)行插入的時(shí)候,它要對(duì)原有的鏈進(jìn)行遍歷绑莺,在遍歷的過(guò)程中發(fā)現(xiàn)可以重用的節(jié)點(diǎn)暖眼,然后不用再二次創(chuàng)建這些節(jié)點(diǎn),在使用中可能會(huì)大大提高性能夠纺裁,因?yàn)檫@個(gè)類是處理高并發(fā)的集合诫肠。

addEntry

    private void addEntry(int hash, K key, V value, int index) {
        modCount++;

        Entry<?,?> tab[] = table;
        //如果數(shù)組長(zhǎng)度大于閾值,則進(jìn)行擴(kuò)容處理
        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];
        //插入節(jié)點(diǎn)欺缘,也是一樣從鏈表頭插入
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
    }

這個(gè)方法也比較簡(jiǎn)單栋豫,跟前面的rehash一樣,插入新節(jié)點(diǎn)是從鏈表頭插入的

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

這個(gè)方法之所以拿出來(lái)講谚殊,我們看第一條判斷語(yǔ)句丧鸯,value==null的判斷,也就是說(shuō)hashtable不支持value為空嫩絮,但是支持key為空丛肢,hashmap既支持key也支持value為空,而ConcurrentHashMap是都不支持二者為空的情況剿干,這個(gè)也是三個(gè)集合類的區(qū)別之一蜂怎。
為什么要這樣規(guī)定key或者value不能為空呢?(網(wǎng)上感覺(jué)比較合理的解釋如下)
ConcurrentHashmap和Hashtable都是支持并發(fā)的置尔,這樣會(huì)有一個(gè)問(wèn)題杠步,當(dāng)你通過(guò)get(k)獲取對(duì)應(yīng)的value時(shí),如果獲取到的是null時(shí)榜轿,你無(wú)法判斷幽歼,它是put(k,v)的時(shí)候value為null,還是這個(gè)key從來(lái)沒(méi)有做過(guò)映射谬盐。HashMap是非并發(fā)的甸私,可以通過(guò)contains(key)來(lái)做這個(gè)判斷。而支持并發(fā)的Map在調(diào)用m.contains(key)和m.get(key),m可能已經(jīng)不同了飞傀。

clone

  //Creates a shallow copy of this hashtable. All the structure of the
// hashtable itself is copied, but the keys and values are not cloned.
  //This is a relatively expensive operation
    public synchronized Object clone() {
        try {
            Hashtable<?,?> t = (Hashtable<?,?>)super.clone();
            t.table = new Entry<?,?>[table.length];
            for (int i = table.length ; i-- > 0 ; ) {
                t.table[i] = (table[i] != null)
                    ? (Entry<?,?>) table[i].clone() : null;
            }
            t.keySet = null;
            t.entrySet = null;
            t.values = null;
            t.modCount = 0;
            return t;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }   

看到方法上的注釋可以知道這個(gè)類跟hashmap一樣都是淺拷貝的颠蕴,key和value值不拷貝泣刹,而相比于此,ConcurrentHashMap是沒(méi)有提供拷貝方法的犀被,也就是不能進(jìn)行拷貝椅您。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市寡键,隨后出現(xiàn)的幾起案子掀泳,更是在濱河造成了極大的恐慌,老刑警劉巖西轩,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件员舵,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡藕畔,警方通過(guò)查閱死者的電腦和手機(jī)马僻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)注服,“玉大人韭邓,你說(shuō)我怎么就攤上這事∪艿埽” “怎么了女淑?”我有些...
    開(kāi)封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)辜御。 經(jīng)常有香客問(wèn)我鸭你,道長(zhǎng),這世上最難降的妖魔是什么擒权? 我笑而不...
    開(kāi)封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任袱巨,我火速辦了婚禮,結(jié)果婚禮上碳抄,老公的妹妹穿的比我還像新娘愉老。我一直安慰自己,他們只是感情好纳鼎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著裳凸,像睡著了一般贱鄙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上姨谷,一...
    開(kāi)封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天逗宁,我揣著相機(jī)與錄音,去河邊找鬼梦湘。 笑死瞎颗,一個(gè)胖子當(dāng)著我的面吹牛件甥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播哼拔,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼引有,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了倦逐?” 一聲冷哼從身側(cè)響起譬正,我...
    開(kāi)封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎檬姥,沒(méi)想到半個(gè)月后曾我,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡健民,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年抒巢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秉犹。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蛉谜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凤优,到底是詐尸還是另有隱情悦陋,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布筑辨,位于F島的核電站俺驶,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏棍辕。R本人自食惡果不足惜暮现,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望楚昭。 院中可真熱鬧栖袋,春花似錦、人聲如沸抚太。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)尿贫。三九已至电媳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間庆亡,已是汗流浹背匾乓。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留又谋,地道東北人拼缝。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓娱局,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親咧七。 傳聞我的和親對(duì)象是個(gè)殘疾皇子衰齐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

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