首頁 Java java教程 java中HashMap和HashTable的差別是什麼? HashMap與HashTable的簡單比較

java中HashMap和HashTable的差別是什麼? HashMap與HashTable的簡單比較

Oct 22, 2018 pm 05:54 PM
hashmap hashtable 集合框架

本篇文章帶給大家的內容是java中HashMap和HashTable的差別是什麼? HashMap和HashTable的簡單比較。有一定的參考價值,有需要的朋友可以參考一下,希望對你們有幫助。

1.首先來看繼承結構:

HashMap

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable
登入後複製

Hashtable

public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable
登入後複製

1、先透過名字我們可以看出HashMap符合駝峰命名規則,Hashtable不符合駝峰命名規則,透過繼承結構我們發現HashMap繼承自AbstractMap,而Hashtable繼承自Dictionnary,Dictionary是一個已經被廢棄的類,所有Hashtable一般不推薦使用,多執行緒環境下可以使用同步容器ConcurrentHashMap類別。

2、通過類別的屬性可以發現,在jdk1.8,當HashMap中的某條鏈中儲存的結點數大於等於8時且鍊錶數組長度大於64時轉化為紅黑樹,而Hashtable並不會轉化為紅黑樹。

3、Hashtable的put()與HashMap的put()

Hashtable的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;
    }
登入後複製

Hashtable中的方法添加了synchronized 關鍵字,所以它是一個同步方法。

透過if (value == null)    throw new NullPointerException();} 可以看出他不允許value的值為空,而當key為null時,呼叫key.hashCode();會拋出null指標異常,所以Hashtable中儲存的Entry中的key和值都不能為空。還有Hashtable取鍊錶下標是透過%運算來取的。

下面看HashMap

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
登入後複製

可以看到HashMap中key可以有一個null,當key為null空時它的hash值為0,而且HashMap取得鍊錶數組下標的方法與Hashtable不同,它是使用(n-1)&hash來計算,因為HashMap的陣列鍊錶長度為2的n次方。

總結:HashMap中的key可以有一個null,值可以有多個null,Hashtable中的key和value都不能為空。定位鏈的方式不同,HashMap透過&運算來取得下標,而Hashtable透過%來取得下標,&運算更快。

4、HashMap和Hashtable的擴容方式不同。

Hashtable擴容:

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

        // overflow-conscious code
        int newCapacity = (oldCapacity << 1) + 1;
        //MAX_ARRAY_SIZE = int的最大值-8
        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;
            }
        }
    }
登入後複製

透過原始碼我們發現Hashtable鍊錶陣列的最大長度為int型別的最大值-8,Hashtable的擴容會把長度變成原來的二倍加1,而且擴容後需要從新定位鍊錶。而且擴容後數組鍊錶的順序變成了原順序的倒序。

HashMap的擴容:

final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        //如果远链表数组长度大于零
        if (oldCap > 0) {
            //如果原长度大于或等于MAXIMUM_CAPACITY(2^30),则将threshold(闸值)设为Integer.MAX_VALUE大约为MAXIMUM_CAPACITY(2^30)的二倍
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            //让新的容量变为旧的二倍
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                 //新的闸值也变为原来的二倍
                newThr = oldThr << 1; // double threshold
        }
        //老的链表数组长度小于等于0,老的闸值大于零,这种情况是初始化时传入一个map导致的构造器为public HashMap(Map<? extends K, ? extends V> m)
        else if (oldThr > 0) // initial capacity was placed in threshold
        	//让新的容量等于旧的闸值
            newCap = oldThr;
         //下面的else是默认的构造器,即public HashMap()
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        //新的闸值为零时(也就是(newCap = oldCap << 1) >= MAXIMUM_CAPACITY的情况),这时需要赋予newThr正确的值。
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;   //闸值=链表数组长度*加载因子。
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        //扩容,如果原来的链表数组中有数据,就复杂到table中
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
            //遍历老的链表数组
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                //当oldTab[j]这条链不为空时
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    //如果这条链只有首节点有数据,把它添加进新链表数组中
                    if (e.next == null)
                        //因为数组的容量时2的n次方,所以使用hash&(newCap-1)来计算出在那条链中。
                        newTab[e.hash & (newCap - 1)] = e;
                     //如果老的链在红黑树中,使用split()方法来复制
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    //当前链中不只只有一个链表头数据时,遍历链表来复制
                    else { // preserve order
                        //数据的复制有两种情况,第一种是原位置不变,第二种是位置改变
         				loHead代表和原链相同位置的链,hiHead代表是原链加上原容量的链,因为扩容后长度为原长度的二倍,一个链中的节点要不在原位置的链中,要么在原位置加原容量的链中
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            //通过e.hash和oldCap进行&运算来得出位置是否需要改变。
                            比如原数组容量为16(10000)和hash值进行&运算,如果高位1未参加运算,则为0即位置不变,如果高位参加了运算值不等于0,需要改变位置。
                           
                        
                            //loHead和hiHead分别代表原位置的链和新位置的链
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            //原位置为j
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            //新位置为j+oldCap
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }
登入後複製

可以看到HashMap的擴容容量變為原來的二倍,而且它不需要從新定位鍊錶,因為擴容後的位置要么在原位置,要么在原位置原容量,透過hash和鍊錶數組的長度進行與運算即可判斷,如果數組長度的高位參加了運算就在原位置原容量,高位沒參加運算在原位置。而且HashMap擴容後鍊錶資料順序不變。

5.HashMap和Hashtable的初始容量不同。
Hashtable的初始容量為11,HashMap的初始容量為16.

以上是java中HashMap和HashTable的差別是什麼? HashMap與HashTable的簡單比較的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

hashmap的擴容機制是什麼 hashmap的擴容機制是什麼 Mar 15, 2023 pm 03:39 PM

hashmap的擴容機制是:重新計算容量,用新的陣列取代原來的陣列。重新計算原始數組的所有資料並插入一個新數組,然後指向新數組;如果數組在容量擴展前已達到最大值,則直接將閾值設為最大整數返回。

如何使用HashMap類別的put()方法將鍵值對插入到HashMap中 如何使用HashMap類別的put()方法將鍵值對插入到HashMap中 Jul 26, 2023 pm 11:53 PM

如何使用HashMap類別的put()方法將鍵值對插入到HashMap中HashMap是Java集合框架中的一個非常重要的類,它提供了一種儲存鍵值對的方式。在實際開發中,我們經常需要在HashMap中插入鍵值對,透過使用HashMap類別的put()方法可以輕鬆實現這一目標。 HashMap的put()方法的簽章如下:Vput(Kkey,Vvalue)

基於Java HashMap,如何解決插入重複的Key值問題 基於Java HashMap,如何解決插入重複的Key值問題 May 09, 2023 am 10:52 AM

javaHashMap插入重複Key值要在HashMap中插入重複的值,首先要先弄清楚HashMap裡面是怎麼存放元素的。 put方法Map裡面存放的每一個元素都是key-value這樣的鍵值對,而且都是透過put方法進行新增的,而且相同的key在Map中只會有一個與之關聯的value存在。 put方法在Map中的定義如下。 Vput(Kkey,Vvalue);put()方法實作:首先hash(key)得到key的hashcode(),hashmap根據所得的hashcode找到要插入的位置所在的鏈,

Java文件解讀:HashMap類別的containsKey()方法用法詳解 Java文件解讀:HashMap類別的containsKey()方法用法詳解 Nov 04, 2023 am 08:12 AM

Java文件解讀:HashMap類別的containsKey()方法用法詳解,需要具體程式碼範例引言:HashMap是Java中常用的資料結構,它提供了高效率的儲存和尋找功能。其中的containsKey()方法用來判斷HashMap中是否包含指定的鍵。本文將詳細解讀HashMap類別的containsKey()方法的使用方式,並提供具體的程式碼範例。一、cont

java中LinkedHashMap和HashMap差別是什麼 java中LinkedHashMap和HashMap差別是什麼 May 02, 2023 am 08:31 AM

1.說明Map基本上可以使用HashMap,但是HashMap有一個問題,那就是迭代HashMap的順序不是HashMap放置的順序,就是無序。 HashMap的這個缺點往往會帶來麻煩,因為有些場景我們期待一個有序的Map,那就是LinkedHashMap。 2.區別實例publicstaticvoidmain(String[]args){Mapmap=newLinkedHashMap();map.put("apple","蘋果");map.put(&quot

Java單例模式怎麼利用HashMap實作快取數據 Java單例模式怎麼利用HashMap實作快取數據 May 13, 2023 am 09:43 AM

一、單例模式是什麼?單例模式是一種物件建立模式,它用於產生一個物件的具體實例,它可以確保系統中一個類別只產生一個實例。 Java裡面實作的單例是一個虛擬機器的範圍,因為裝載類別的功能是虛擬機器的,所以一個虛擬機器在透過自己的ClassLoad裝載實作單例類別的時候就會建立一個類別的實例。在Java語言中,這樣的行為能帶來兩大好處:1.對於頻繁使用的對象,可以省略創建對象所花費的時間,這對於那些重量級對象而言,是非常可觀的一筆系統開銷; 2.由於new操作的次數減少,因而對系統記憶體的使用頻率也會降低,這將減輕GC壓

Java使用HashMap類別的putAll()函數將一個Map加入另一個Map Java使用HashMap類別的putAll()函數將一個Map加入另一個Map Jul 24, 2023 am 09:36 AM

Java使用HashMap類別的putAll()函數將一個Map加入到另一個Map中Map是Java中常用的資料結構,用來表示鍵值對的集合。在Java的集合框架中,HashMap是一個常用的實作類別。它提供了putAll()函數,用於將一個Map添加到另一個Map中,以方便實現資料的合併和拷貝。本文將介紹putAll()函數的使用方法,並提供對應的程式碼範例。首先,

Java Map 效能優化揭秘:讓你的資料操作更快速、更有效率 Java Map 效能優化揭秘:讓你的資料操作更快速、更有效率 Feb 20, 2024 am 08:31 AM

JavaMap是Java標準函式庫中常用的資料結構,它以鍵值對的形式儲存資料。 Map的效能對於應用程式的運作效率至關重要,如果Map的效能不佳,可能會導致應用程式運作緩慢,甚至崩潰。 1.選擇合適的Map實作Java提供了多種Map實現,包括HashMap、TreeMap和LinkedHashMap。每種Map實作都有各自的優缺點,在選擇Map實作時,需要根據應用程式的特定需求來選擇合適的實作。 HashMap:HashMap是最常用的Map實現,它使用哈希表來儲存數據,具有較快的插入、刪除和查找速度

See all articles