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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

hashmap删除指定key_HashTable和HashMap的区别详解

發布時間:2023/12/19 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 hashmap删除指定key_HashTable和HashMap的区别详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、HashMap簡介

? ? ? HashMap是基于哈希表實現的,每一個元素是一個key-value對,其內部通過單鏈表解決沖突問題,容量不足(超過了閥值)時,同樣會自動增長。

? ? ? HashMap是非線程安全的,只是用于單線程環境下,多線程環境下可以采用concurrent并發包下的concurrentHashMap。

? ? ? HashMap 實現了Serializable接口,因此它支持序列化,實現了Cloneable接口,能被克隆。

? ? ? HashMap存數據的過程是:

? ? ? HashMap內部維護了一個存儲數據的Entry數組,HashMap采用鏈表解決沖突,每一個Entry本質上是一個單向鏈表。當準備添加一個key-value對時,首先通過hash(key)方法計算hash值,然后通過indexFor(hash,length)求該key-value對的存儲位置,計算方法是先用hash&0x7FFFFFFF后,再對length取模,這就保證每一個key-value對都能存入HashMap中,當計算出的位置相同時,由于存入位置是一個鏈表,則把這個key-value對插入鏈表頭。

? ? ? HashMap中key和value都允許為null。key為null的鍵值對永遠都放在以table[0]為頭結點的鏈表中。

? ? ??了解了數據的存儲,那么數據的讀取也就很容易就明白了。

HashMap的存儲結構,如下圖所示:

? 圖中,紫色部分即代表哈希表,也稱為哈希數組,數組的每個元素都是一個單鏈表的頭節點,鏈表是用來解決沖突的,如果不同的key映射到了數組的同一位置處,就將其放入單鏈表中。

? ? ? HashMap內存儲數據的Entry數組默認是16,如果沒有對Entry擴容機制的話,當存儲的數據一多,Entry內部的鏈表會很長,這就失去了HashMap的存儲意義了。所以HasnMap內部有自己的擴容機制。HashMap內部有:

? ? ??變量size,它記錄HashMap的底層數組中已用槽的數量;

? ? ??變量threshold,它是HashMap的閾值,用于判斷是否需要調整HashMap的容量(threshold?=?容量*加載因子)????

? ? ??變量DEFAULT_LOAD_FACTOR?=?0.75f,默認加載因子為0.75

? ? ? HashMap擴容的條件是:當size大于threshold時,對HashMap進行擴容??

? ? ??擴容是是新建了一個HashMap的底層數組,而后調用transfer方法,將就HashMap的全部元素添加到新的HashMap中(要重新計算元素在新的數組中的索引位置)。?很明顯,擴容是一個相當耗時的操作,因為它需要重新計算這些元素在新的數組中的位置并進行復制處理。因此,我們在用HashMap的時,最好能提前預估下HashMap中元素的個數,這樣有助于提高HashMap的性能。

? ? ? HashMap共有四個構造方法。構造方法中提到了兩個很重要的參數:初始容量和加載因子。這兩個參數是影響HashMap性能的重要參數,其中容量表示哈希表中槽的數量(即哈希數組的長度),初始容量是創建哈希表時的容量(從構造函數中可以看出,如果不指明,則默認為16),加載因子是哈希表在其容量自動增加之前可以達到多滿的一種尺度,當哈希表中的條目數超出了加載因子與當前容量的乘積時,則要對該哈希表進行 resize 操作(即擴容)。

? ? ??下面說下加載因子,如果加載因子越大,對空間的利用更充分,但是查找效率會降低(鏈表長度會越來越長);如果加載因子太小,那么表中的數據將過于稀疏(很多空間還沒用,就開始擴容了),對空間造成嚴重浪費。如果我們在構造方法中不指定,則系統默認加載因子為0.75,這是一個比較理想的值,一般情況下我們是無需修改的。

?? ? ??另外,無論我們指定的容量為多少,構造方法都會將實際容量設為不小于指定容量的2的次方的一個數,且最大值不能超過2的30次方

? ? ??對HashMap想進一步深入了解的朋友推薦看一下HashMap源碼剖析:http://blog.csdn.net/ns_code/article/details/36034955

二、Hashtable簡介

? ? ? Hashtable同樣是基于哈希表實現的,同樣每個元素是一個key-value對,其內部也是通過單鏈表解決沖突問題,容量不足(超過了閥值)時,同樣會自動增長。

? ? ? Hashtable也是JDK1.0引入的類,是線程安全的,能用于多線程環境中。

? ? ? Hashtable同樣實現了Serializable接口,它支持序列化,實現了Cloneable接口,能被克隆。

? ? ? Hashtable和HashMap比較相似,感興趣的朋友可以看“Hashtable源碼剖析”這篇博客:http://blog.csdn.net/ns_code/article/details/36191279

下面主要介紹一下HashTable和HashMap區別

三、HashTable和HashMap區別

? ? ??1、繼承的父類不同

? ? ? Hashtable繼承自Dictionary類,而HashMap繼承自AbstractMap類。但二者都實現了Map接口。

? ? ??2、線程安全性不同

? ? ? javadoc中關于hashmap的一段描述如下:此實現不是同步的。如果多個線程同時訪問一個哈希映射,而其中至少一個線程從結構上修改了該映射,則它必須保持外部同步。

? ? ? Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省情況下是非Synchronize的。在多線程并發的環境下,可以直接使用Hashtable,不需要自己為它的方法實現同步,但使用HashMap時就必須要自己增加同步處理。(結構上的修改是指添加或刪除一個或多個映射關系的任何操作;僅改變與實例已經包含的鍵關聯的值不是結構上的修改。)這一般通過對自然封裝該映射的對象進行同步操作來完成。如果不存在這樣的對象,則應該使用 Collections.synchronizedMap 方法來“包裝”該映射。最好在創建時完成這一操作,以防止對映射進行意外的非同步訪問,如下所示:

? ? ??Map m = Collections.synchronizedMap(new HashMap(...));

? ? ? Hashtable 線程安全很好理解,因為它每個方法中都加入了Synchronize。這里我們分析一下HashMap為什么是線程不安全的:

? ? ? HashMap底層是一個Entry數組,當發生hash沖突的時候,hashmap是采用鏈表的方式來解決的,在對應的數組位置存放鏈表的頭結點。對鏈表而言,新加入的節點會從頭結點加入。

我們來分析一下多線程訪問:

? ? ??(1)在hashmap做put操作的時候會調用下面方法:

  • //?新增Entry。將“key-value”插入指定位置,bucketIndex是位置索引。??????

  • ????void?addEntry(int?hash,?K?key,?V?value,?int?bucketIndex)?{??????

  • ????????//?保存“bucketIndex”位置的值到“e”中??????

  • ????????Entry?e?=?table[bucketIndex];??????

  • ????????//?設置“bucketIndex”位置的元素為“新Entry”,??????

  • ????????//?設置“e”為“新Entry的下一個節點”??????

  • ????????table[bucketIndex]?=?new?Entry(hash,?key,?value,?e);??????

  • ????????//?若HashMap的實際大小?不小于?“閾值”,則調整HashMap的大小??????

  • ????????if?(size++?>=?threshold)??????

  • ????????????resize(2?*?table.length);??????

  • ????}??

  • ? ? ??在hashmap做put操作的時候會調用到以上的方法。現在假如A線程和B線程同時對同一個數組位置調用addEntry,兩個線程會同時得到現在的頭結點,然后A寫入新的頭結點之后,B也寫入新的頭結點,那B的寫入操作就會覆蓋A的寫入操作造成A的寫入操作丟失

    (? ? ??2)刪除鍵值對的代碼

  • "font-size:?18px;">??????//?刪除“鍵為key”的元素??????

  • ????final?Entry?removeEntryForKey(Object?key)?{??????

  • ????????//?獲取哈希值。若key為null,則哈希值為0;否則調用hash()進行計算??????

  • ????????int?hash?=?(key?==?null)???0?:?hash(key.hashCode());??????

  • ????????int?i?=?indexFor(hash,?table.length);??????

  • ????????Entry?prev?=?table[i];??????

  • ????????Entry?e?=?prev;??????

  • ????????//?刪除鏈表中“鍵為key”的元素??????

  • ????????//?本質是“刪除單向鏈表中的節點”??????

  • ????????while?(e?!=?null)?{??????

  • ????????????Entry?next?=?e.next;??????

  • ????????????Object?k;??????

  • ????????????if?(e.hash?==?hash?&&??????

  • ????????????????((k?=?e.key)?==?key?||?(key?!=?null?&&?key.equals(k))))?{??????

  • ????????????????modCount++;??????

  • ????????????????size--;??????

  • ????????????????if?(prev?==?e)??????

  • ????????????????????table[i]?=?next;??????

  • ????????????????else?????

  • ????????????????????prev.next?=?next;??????

  • ????????????????e.recordRemoval(this);??????

  • ????????????????return?e;??????

  • ????????????}??????

  • ????????????prev?=?e;??????

  • ????????????e?=?next;??????

  • ????????}??????

  • ????????return?e;??????

  • ????}??

  • ? 當多個線程同時操作同一個數組位置的時候,也都會先取得現在狀態下該位置存儲的頭結點,然后各自去進行計算操作,之后再把結果寫會到該數組位置去,其實寫回的時候可能其他的線程已經就把這個位置給修改過了,就會覆蓋其他線程的修改

    ? ? ??(3)addEntry中當加入新的鍵值對后鍵值對總數量超過門限值的時候會調用一個resize操作,代碼如下:

  • //?重新調整HashMap的大小,newCapacity是調整后的容量??????

  • ????void?resize(int?newCapacity)?{??????

  • ????????Entry[]?oldTable?=?table;??????

  • ????????int?oldCapacity?=?oldTable.length;?????

  • ????????//如果就容量已經達到了最大值,則不能再擴容,直接返回????

  • ????????if?(oldCapacity?==?MAXIMUM_CAPACITY)?{??????

  • ????????????threshold?=?Integer.MAX_VALUE;??????

  • ????????????return;??????

  • ????????}??????

  • ????????//?新建一個HashMap,將“舊HashMap”的全部元素添加到“新HashMap”中,??????

  • ????????//?然后,將“新HashMap”賦值給“舊HashMap”。??????

  • ????????Entry[]?newTable?=?new?Entry[newCapacity];??????

  • ????????transfer(newTable);??????

  • ????????table?=?newTable;??????

  • ????????threshold?=?(int)(newCapacity?*?loadFactor);??????

  • ????}??

  • ? ? ??這個操作會新生成一個新的容量的數組,然后對原數組的所有鍵值對重新進行計算和寫入新的數組,之后指向新生成的數組。

    ? 當多個線程同時檢測到總數量超過門限值的時候就會同時調用resize操作,各自生成新的數組并rehash后賦給該map底層的數組table,結果最終只有最后一個線程生成的新數組被賦給table變量,其他線程的均會丟失。而且當某些線程已經完成賦值而其他線程剛開始的時候,就會用已經被賦值的table作為原始數組,這樣也會有問題。

    ? ? ??3、是否提供contains方法

    ? ? ? HashMap把Hashtable的contains方法去掉了,改成containsValue和containsKey,因為contains方法容易讓人引起誤解。

    ? ? ? Hashtable則保留了contains,containsValue和containsKey三個方法,其中contains和containsValue功能相同。

    我們看一下Hashtable的ContainsKey方法和ContainsValue的源碼:

  • public?boolean?containsValue(Object?value)?{??????

  • ?????return?contains(value);??????

  • ?}??

  • //?判斷Hashtable是否包含“值(value)”??????

  • ?public?synchronized?boolean?contains(Object?value)?{??????

  • ?????//注意,Hashtable中的value不能是null,??????

  • ?????//?若是null的話,拋出異常!??????

  • ?????if?(value?==?null)?{??????

  • ?????????throw?new?NullPointerException();??????

  • ?????}??????

  • ?????//?從后向前遍歷table數組中的元素(Entry)??????

  • ?????//?對于每個Entry(單向鏈表),逐個遍歷,判斷節點的值是否等于value??????

  • ?????Entry?tab[]?=?table;??????

  • ?????for?(int?i?=?tab.length?;?i--?>?0?;)?{??????

  • ?????????for?(Entry?e?=?tab[i]?;?e?!=?null?;?e?=?e.next)?{??????

  • ?????????????if?(e.value.equals(value))?{??????

  • ?????????????????return?true;??????

  • ?????????????}??????

  • ?????????}??????

  • ?????}??????

  • ?????return?false;??????

  • ?}??

  • //?判斷Hashtable是否包含key??????

  • ?public?synchronized?boolean?containsKey(Object?key)?{??????

  • ?????Entry?tab[]?=?table;??????

  • /計算hash值,直接用key的hashCode代替????

  • ?????int?hash?=?key.hashCode();????????

  • ?????//?計算在數組中的索引值?????

  • ?????int?index?=?(hash?&?0x7FFFFFFF)?%?tab.length;??????

  • ?????//?找到“key對應的Entry(鏈表)”,然后在鏈表中找出“哈希值”和“鍵值”與key都相等的元素??????

  • ?????for?(Entry?e?=?tab[index]?;?e?!=?null?;?e?=?e.next)?{??????

  • ?????????if?((e.hash?==?hash)?&&?e.key.equals(key))?{??????

  • ?????????????return?true;??????

  • ?????????}??????

  • ?????}??????

  • ?????return?false;??????

  • ?}??

  • ? ? ??下面我們看一下HashMap的ContainsKey方法和ContainsValue的源碼:

  • //?HashMap是否包含key??????

  • ????public?boolean?containsKey(Object?key)?{??????

  • ????????return?getEntry(key)?!=?null;??????

  • ????}??

  • //?返回“鍵為key”的鍵值對??????

  • ????final?Entry?getEntry(Object?key)?{??????

  • ????????//?獲取哈希值??????

  • ????????//?HashMap將“key為null”的元素存儲在table[0]位置,“key不為null”的則調用hash()計算哈希值??????

  • ????????int?hash?=?(key?==?null)???0?:?hash(key.hashCode());??????

  • ????????//?在“該hash值對應的鏈表”上查找“鍵值等于key”的元素??????

  • ????????for?(Entry?e?=?table[indexFor(hash,?table.length)];??????

  • ?????????????e?!=?null;??????

  • ?????????????e?=?e.next)?{??????

  • ????????????Object?k;??????

  • ????????????if?(e.hash?==?hash?&&??????

  • ????????????????((k?=?e.key)?==?key?||?(key?!=?null?&&?key.equals(k))))??????

  • ????????????????return?e;??????

  • ????????}??????

  • ????????return?null;??????

  • ????}??

  • //?是否包含“值為value”的元素??????

  • ????public?boolean?containsValue(Object?value)?{??????

  • ????//?若“value為null”,則調用containsNullValue()查找??????

  • ????if?(value?==?null)??????

  • ????????????return?containsNullValue();??????

  • ????//?若“value不為null”,則查找HashMap中是否有值為value的節點。??????

  • ????Entry[]?tab?=?table;??????

  • ????????for?(int?i?=?0;?i?

  • ????????????for?(Entry?e?=?tab[i]?;?e?!=?null?;?e?=?e.next)??????

  • ????????????????if?(value.equals(e.value))??????

  • ????????????????????return?true;??????

  • ????return?false;??????

  • ????}??

  • 通過上面源碼的比較,我們可以得到第四個不同的地方

    ? ? ??4、key和value是否允許null值

    ? ? ??其中key和value都是對象,并且不能包含重復key,但可以包含重復的value。

    ? ? ??通過上面的ContainsKey方法和ContainsValue的源碼我們可以很明顯的看出:

    ? ? ? Hashtable中,key和value都不允許出現null值。但是如果在Hashtable中有類似put(null,null)的操作,編譯同樣可以通過,因為key和value都是Object類型,但運行時會拋出NullPointerException異常,這是JDK的規范規定的。HashMap中,null可以作為鍵,這樣的鍵只有一個;可以有一個或多個鍵所對應的值為null。當get()方法返回null值時,可能是 HashMap中沒有該鍵,也可能使該鍵所對應的值為null。因此,在HashMap中不能由get()方法來判斷HashMap中是否存在某個鍵, 而應該用containsKey()方法來判斷。

    ? ? ??5、兩個遍歷方式的內部實現上不同

    ? ? ? Hashtable、HashMap都使用了 Iterator。而由于歷史原因,Hashtable還使用了Enumeration的方式 。

    ? ? ??6、hash值不同

    ? ? ??哈希值的使用不同,HashTable直接使用對象的hashCode。而HashMap重新計算hash值。

    ? ? ? hashCode是jdk根據對象的地址或者字符串或者數字算出來的int類型的數值。

    ? ? ? Hashtable計算hash值,直接用key的hashCode(),而HashMap重新計算了key的hash值,Hashtable在求hash值對應的位置索引時,用取模運算,而HashMap在求位置索引時,則用與運算,且這里一般先用hash&0x7FFFFFFF后,再對length取模,&0x7FFFFFFF的目的是為了將負的hash值轉化為正值,因為hash值有可能為負數,而&0x7FFFFFFF后,只有符號外改變,而后面的位都不變。

    ? ? ??7、內部實現使用的數組初始化和擴容方式不同

    ? ? ? HashTable在不指定容量的情況下的默認容量為11,而HashMap為16,Hashtable不要求底層數組的容量一定要為2的整數次冪,而HashMap則要求一定為2的整數次冪。? ? ? Hashtable擴容時,將容量變為原來的2倍加1,而HashMap擴容時,將容量變為原來的2倍。

    ? ? ? Hashtable和HashMap它們兩個內部實現方式的數組的初始大小和擴容的方式。HashTable中hash數組默認大小是11,增加的方式是 old*2+1。

    總結

    以上是生活随笔為你收集整理的hashmap删除指定key_HashTable和HashMap的区别详解的全部內容,希望文章能夠幫你解決所遇到的問題。

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

    主站蜘蛛池模板: 日本久久亚洲 | 尤物视频免费观看 | 天堂av成人 | 久草综合在线观看 | 欧美视频 | 色香蕉在线视频 | 久草视频国产 | 欧美第十页 | 免费在线不卡av | 国产精品一区二区在线 | 欧美一级免费看 | 最新黄色网址在线观看 | 国产精品美女久久久久av爽 | 亚洲v日韩v综合v精品v | 欧美色xxx | va婷婷 | 就操网| 国产精品性 | 欧美日韩激情一区 | 九九视频在线免费观看 | 免费不卡av| 欧美激情综合色综合啪啪五月 | 中文字幕人妻互换av久久 | 91青青青| 成人无码精品1区2区3区免费看 | 国产一级av毛片 | 日韩欧美一区二区三区免费观看 | 日免费视频 | 国产性生活视频 | 国产精品13p | 国产精品无码av在线播放 | 国产伦精品一区二区三区免费迷 | 波多野结衣av在线免费观看 | 让男按摩师摸好爽 | 亚洲天堂av一区二区 | 婷婷激情五月网 | 又黄又色又爽的视频 | 97久久人人超碰caoprom欧美 | 草草在线视频 | 国产又黄视频 | 亚洲视频成人 | 亚洲综合在线视频 | av在线免费观看一区 | 九九这里只有精品视频 | 一区二区三区免费看 | 欧美在线激情视频 | 日韩天堂在线 | 在线看片中文字幕 | 扒丝袜| 91直接进入 | 国产123区在线观看 91国产一区二区 | 国产免费内射又粗又爽密桃视频 | 成人免费看黄 | 51成人精品网站 | 国产区一区二区 | 青青色在线 | 永久视频在线观看 | 北条麻妃在线一区二区 | 久久久久亚洲av成人片 | 国产黄在线免费观看 | 亚洲特级片 | 国产夫妻自拍小视频 | 久久欧美 | 少妇免费毛片久久久久久久久 | 国精品无码人妻一区二区三区 | 香港三级日本三级 | 91精品色 | 成人午夜大片 | 国产免费久久久 | 国产精品久久久久无码av | 国产91丝袜在线播放0 | 国产精品扒开腿做爽爽爽男男 | 夜夜涩 | 理论在线视频 | 制服.丝袜.亚洲.中文.综合懂 | av网站在线免费播放 | 黄视频在线观看免费 | 欧美裸体女人 | 精品91自产拍在线观看二区 | 欧美性视频播放 | 日韩成人综合 | 搡老熟女国产 | 荒野求生21天去码版网站 | 人人草网站 | 国产无码精品一区二区 | youjizz.com在线观看 | 久久久久国产精品区片区无码 | 天天干夜操 | 欧洲女性下面有没有毛发 | 精品少妇v888av | 免费av视屏 | 国产日韩在线免费观看 | 国产乱淫av一区二区三区 | 欧美夫妇交换xxx | 天堂bt在线| 色欲色香天天天综合网www | 日韩视频在线一区 | 日韩av在线一区 | 天天看天天爽 |