日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

深入分析HashMap与Hashtable的区别

發布時間:2024/9/18 综合教程 39 生活家
生活随笔 收集整理的這篇文章主要介紹了 深入分析HashMap与Hashtable的区别 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、前言

??上個月花了點時間研究了一下HashMap的源碼,對HashMap的實現原理有了一個較為深入的了解,今天突然想到有一個常考的面試題——HashMapHashtable的區別,于是又花了點時間研究了一下Hashtable的源碼。今天這篇博客就來簡單介紹一下兩者的區別,以下內容將主要從Hashtable的角度,描述它和HashMap有什么不同,而且將分兩個方面來敘述——使用方面實現細節方面。如果對HashMap不是很了解的,可以閱讀一下這篇博客:HashMap源碼解讀——深入理解HashMap高效的原因。

二、正文

?2.1 使用上的不同

?(1)Hashtable線程安全,HashMap線程不安全

??這是這兩個容器最根本的區別。HashMap是一個線程不安全的容器,它并沒有實現線程同步,所以不應該在多線程的環境下使用。而Hashtable實現了線程同步,是一個線程安全的容器,但是它實現同步的方式比較拙劣——直接在大部分的public方法上添加synchronized關鍵字進行同步,這樣對于同一個Hashtable對象,每次只能有一個線程調用它的實例方法。這種拙劣的同步方式使得Hashtable的效率要遠遠低于HashMap,而且在很多情況下,這種同步是沒有意義的,比如在沒有寫操作發生的情況下,完全可以多個線程同時對Hashtable進行讀操作,并不會造成危害。

?(2)Hashtable不允許key或value為null,HashMap可以

??在Hashtable的很多方法中(比如put方法),特判了valuenull的情況,此時將直接拋出一個NullPointerException;而對于keynull的情況卻沒有特判,但是若在其中加入一個keynull的元素,依舊會拋出NullPointerException,因為Hashtable中獲取keyhash值是直接調用key.hashCode方法,所有key不能為null。我們看Hashtable中的put方法即可知曉(多余的代碼被刪除):

public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) {
        throw new NullPointerException();
    }

    // ......
    int hash = key.hashCode();

    // ......
}

??但是HashMap中,無論是key還是value,都可以為null,因為它對null做了特殊處理。HashMap實現中,判斷keynull,則不會調用key.hashCode獲取它的hash值,而是將0作為它的hash值;而在get方法獲取value時,如果找到的節點為null,則直接返回null,否則返回node.value。同樣看HashMap中的兩段代碼來驗證這一點:

// 此方法為HashMap中獲取key的hash值的方法,特判了key == null的情況
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

// HashMap中的get方法,特判了value為null的情況
public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

?2.2 實現細節上的不同

?(1)Hashtable使用頭插法,JDK1.8之后的HashMap使用尾插法

??先簡單說一說什么是頭插和尾插:

頭插法:在鏈表中插入一個新的節點時,將新節點作為鏈表的頭節點,然后將新節點的next指向舊頭節點;
尾插法:在鏈表中插入一個新的節點時,讓鏈表的尾節點的next指向新節點;

??Hashtable使用的是頭插法,而HashMapJDK1.8之前,使用的也是頭插法。但是,有人發現在并發情況下,若沒有進行線程同步,頭插法有很小的概率會導致死循環(這個問題我就不展開描述了,可以自己去查一查),于是從JDK1.8開始,為HashMap添加節點改為了尾插法。

?(2)HashMap的容量必須是2^n,而Hashtable的容量沒有這個限制

??這是兩者之間非常大的一個區別,就這一點,讓HashMapHashtble在實現上有很大的不同。HashMap的容量一定是2^n,其原因我在前言中推薦的博客里面做了詳細的描述,這里就簡單說一下。容量定為2^n是為了對取余運算做優化,因為2^n滿足一個性質:

num % 2^n == num & (2^n - 1)

??不論是HashMap還是Hashtable,要獲取節點在數組中的下標,都需要用keyhash值對數組長度取余,而HashMap將數組長度限制為2^n,就是為了用上面公式中的&替代%進行取余操作,因為&的效率要比%更高,而獲取下標又是一個頻繁的操作,所以這一步優化讓HashMap的查找速度有了很大的提升。而在Hashtable中,就是直接用%取余。我們看兩者取余的源碼:

// HashMap中的取余:tab為存放節點的數組,n為數組長度,hash變量存的是key的哈希值
// 這一步就是獲取哈希值為hash的key在tab中的位置
// HashMap中的hash值不會是負數,因為HashMap中自己實現了hash方法求hash值
tab[(n - 1) & hash]

// Hashtable中的取余:hash & 0x7FFFFFFF其實就是取hash的絕對值,然后直接對tab.length取余
// 0x7FFFFFFF轉換成二進制就是第一位為0,后31位為1,這個操作也就是將符號位轉為0(第一位為符號位),
// 而二進制中,符號位為0表示表示正數,這一步是防止計算出負數的下標,因為hash值可能為負
// 不用Math.abs是因為Math.abs(Integer.MAX_VALUE)越界,結果還是負數
int index = (hash & 0x7FFFFFFF) % tab.length;

??而正是這個優化,導致了HashMapHashtable的許多其他區別:

HashMap的默認初始容量為16,也就是2^4,如果指定了一個容量,則HashMap不會直接用它,而是先找出第一個大于這個指定容量的2^n;而Hashtable的默認初始容量是1111是一個質數,根據hash求下標時,質數能夠讓下標分配更加均勻,Hashtable會直接接收指定的初始容量;
HashMap擴容時直接將數組大小乘2,因為(2^n)×2 == 2^(n+1),仍然滿足2^n;而Hashtable擴容是乘2加1,因為這樣可以讓一個質數很大概率還是一個質數;

?(3)JDK1.8開始,HashMap結構變為數組+鏈表+紅黑樹

??在JDK1.7以前,HashMapHashtable的結構都是數組+鏈表的實現,但是從JDK1.8開始,HashMap變成了數組+鏈表+紅黑樹。這是因為,在hash取余的結果不均勻的情況下,節點可能集中在數組中的某幾條鏈表上,導致鏈表過長。比如舉一個極端的例子,所有的節點通過hash值計算出的下標都相等,此時所有的節點都在一條鏈表上,這個時候HashMap或者Hashtable就退化成了一個鏈表,查詢的時間復雜度退化為了O(n)。為了避免這種情況的發生,從JDK1.8開始,HashMap中若某條鏈表的節點數達到了8個,就會將這條鏈表轉換為紅黑樹,此時,對這條鏈表的查詢效率就從O(n)提升為O(logn)。而Hashtable作為一個已經過時的容器,自然不會花時間對它進行復雜的優化。

三、總結

??Hashtable是一個過時的容器,在開發當中,我們應該少去使用它,甚至根本不要使用它。在單線程的環境下,我們最好使用HashMap,而在多線程的環境下,應該使用ConcurrentHashMap,它的效率要遠遠高于Hashtable。上面最后提到的幾點都和HashMap的實現有關,建議閱讀我在前言中推薦的博客,了解HashMap的具體實現。

四、參考

https://blog.csdn.net/weixin_43871333/article/details/100888713

總結

以上是生活随笔為你收集整理的深入分析HashMap与Hashtable的区别的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。