最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中的HashMap和ConcurrentHashMap區(qū)別和適用場景

 更新時(shí)間:2025年01月11日 09:09:57   作者:繁川落雨  
HashMap和ConcurrentHashMap在對null值的處理、線程安全性、性能等方面存在顯著的區(qū)別,HashMap允許鍵和值為null,適用于單線程環(huán)境下的數(shù)據(jù)存儲和查詢場景;而ConcurrentHashMap不允許鍵和值為null,適用多線程環(huán)境下的數(shù)據(jù)存儲和查詢場景,具有線程安全性和較高的并發(fā)性能

HashMap和ConcurrentHashMap在對null值的處理、線程安全性、性能等方面存在顯著的區(qū)別。HashMap允許鍵和值為null,適用于單線程環(huán)境下的數(shù)據(jù)存儲和查詢場景,具有較高的性能和簡單的使用方式;而ConcurrentHashMap不允許鍵和值為null,適用于多線程環(huán)境下的數(shù)據(jù)存儲和查詢場景,具有線程安全性和較高的并發(fā)性能。 

1. 對null值的處理

1.1 HashMap對null值的處理

HashMap允許鍵(key)和值(value)都為null。這種設(shè)計(jì)使得HashMap在某些場景下更加靈活。例如,在處理一些可能存在空值的數(shù)據(jù)源時(shí),可以直接將數(shù)據(jù)存儲到HashMap中,而不需要額外的非空判斷或轉(zhuǎn)換。以下是一個(gè)簡單的示例代碼:

HashMap<String, Object> map = new HashMap<>();
map.put(null, null); // 正常執(zhí)行,key 和 value 都為 null
if (map.containsKey(null)) {
    System.out.println("存在 null");
} else {
    System.out.println("不存在 null");
}

執(zhí)行上述代碼,控制臺會輸出“存在 null”,表明HashMap成功地將null作為鍵和值存儲,并且可以通過containsKey(null)方法準(zhǔn)確地判斷出null鍵的存在。

1.2 ConcurrentHashMap對null值的處理

與HashMap不同,ConcurrentHashMap不允許鍵(key)和值(value)為null。如果嘗試將null作為鍵或值插入到ConcurrentHashMap中,會拋出NullPointerException異常。以下是兩個(gè)示例代碼:

ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap<>();
concurrentHashMap.put(null, "javacn.site"); // 拋出 NullPointerException
String key = "www.Javacn.site";
ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap<>();
concurrentHashMap.put(key, null); // 拋出 NullPointerException

在上述兩個(gè)代碼片段中,無論是將null作為鍵還是值插入到ConcurrentHashMap中,都會導(dǎo)致程序異常終止,并拋出NullPointerException異常。

1.3 為什么ConcurrentHashMap不能插入null?

要理解ConcurrentHashMap為什么不能插入null值,我們需要從其源碼層面進(jìn)行分析。以下是ConcurrentHashMap添加元素時(shí)的部分核心源碼:

// 添加 key 和 value
public V put(K key, V value) {
    return putVal(key, value, false);
}

final V putVal(K key, V value, boolean onlyIfAbsent) {
    // 如果 key 或 value 為 null 的話直接拋出空指針異常
    if (key == null || value == null) throw new NullPointerException();
    int hash = spread(key.hashCode());
    int binCount = 0;
    // 忽略其他代碼......
}

從上述源碼可以看出,在putVal方法的第一行,ConcurrentHashMap就對key和value進(jìn)行了null檢查,如果發(fā)現(xiàn)它們中的任何一個(gè)為null,就會直接拋出NullPointerException異常。這種設(shè)計(jì)是ConcurrentHashMap的一個(gè)明確的約束條件,旨在避免因null值引起的潛在問題。

1.4 更深層次的原因

那么,為什么ConcurrentHashMap的實(shí)現(xiàn)源碼中要明確禁止key或value為null呢?這要從ConcurrentHashMap的使用場景和并發(fā)環(huán)境下的特殊性來分析。

1.4.1 二義性問題

在并發(fā)環(huán)境下,如果允許ConcurrentHashMap的key或value為null,就會存在經(jīng)典的“二義性問題”。二義性問題指的是代碼或表達(dá)式存在多種理解或解釋,導(dǎo)致程序的含義不明確或模糊。對于ConcurrentHashMap來說,null值的二義性主要體現(xiàn)在以下兩個(gè)方面:

  • null作為具體值:當(dāng)我們將null作為值存儲到ConcurrentHashMap中時(shí),null表示的就是一個(gè)具體的“null”值狀態(tài)。例如,某個(gè)業(yè)務(wù)場景中,某個(gè)屬性的值可能就是null,表示該屬性沒有具體的值或者不適用。
  • null表示“沒有”:在某些情況下,null還可以表示“沒有”的意思,即某個(gè)鍵在ConcurrentHashMap中根本沒有對應(yīng)的值。例如,當(dāng)我們查詢ConcurrentHashMap時(shí),如果返回null,可能是因?yàn)樵撴I從未被插入過,或者該鍵對應(yīng)的值確實(shí)就是null。

如果ConcurrentHashMap允許插入null值,那么在并發(fā)環(huán)境下,當(dāng)我們查詢某個(gè)鍵時(shí),得到的null值就無法明確區(qū)分是上述哪種情況,從而導(dǎo)致二義性問題。

1.4.2 HashMap的可證偽性

相比之下,HashMap允許插入null值,但它不怕二義性問題的原因在于,HashMap是為單線程環(huán)境設(shè)計(jì)的。在單線程環(huán)境下,二義性問題是可被證明真?zhèn)蔚?。例如,?dāng)我們給HashMap的key設(shè)置為null時(shí),可以通過hashMap.containsKey(key)的方法來區(qū)分這個(gè)null值到底是存入的null,還是壓根不存在的null。因?yàn)閱尉€程環(huán)境下,數(shù)據(jù)的修改和查詢是順序執(zhí)行的,不會受到其他線程的干擾,所以二義性問題可以被明確地解決。

1.4.3 ConcurrentHashMap的不可證偽性

而ConcurrentHashMap是為多線程環(huán)境設(shè)計(jì)的,多線程下的二義性問題是不能被證明真?zhèn)蔚?。因?yàn)樵谧C明二義性問題的同時(shí),可能會有其他線程影響執(zhí)行結(jié)果,導(dǎo)致結(jié)果不準(zhǔn)確。例如,當(dāng)ConcurrentHashMap未設(shè)置key為null時(shí),可能會出現(xiàn)以下場景:一個(gè)線程A調(diào)用了concurrentHashMap.containsKey(key),期望返回的結(jié)果是false,但在調(diào)用該方法之后,未返回結(jié)果之前,線程B又調(diào)用了concurrentHashMap.put(key, null)存入了null值,那么線程A最終返回的結(jié)果就是true了。這個(gè)結(jié)果與線程A之前預(yù)想的false完全不一樣,這就是不能被證偽的二義性問題。

為了避免這種復(fù)雜的二義性問題,ConcurrentHashMap選擇在源碼中明確禁止null值作為key或value,從而簡化了并發(fā)環(huán)境下的數(shù)據(jù)管理邏輯,確保了數(shù)據(jù)的一致性和準(zhǔn)確性。

2. 線程安全性

2.1 HashMap的線程安全性

HashMap是非線程安全的,這意味著在多線程環(huán)境下使用HashMap時(shí),可能會遇到各種并發(fā)問題,如數(shù)據(jù)丟失、數(shù)據(jù)重復(fù)、死鎖等。具體來說,HashMap的線程不安全性主要體現(xiàn)在以下幾個(gè)方面:

  • 數(shù)據(jù)丟失:當(dāng)多個(gè)線程同時(shí)對HashMap進(jìn)行插入操作時(shí),可能會出現(xiàn)數(shù)據(jù)丟失的情況。例如,兩個(gè)線程同時(shí)插入相同的鍵但不同的值,最終可能會導(dǎo)致只有一個(gè)值被成功插入,而另一個(gè)值被覆蓋。
  • 數(shù)據(jù)重復(fù):在某些情況下,多個(gè)線程可能會插入重復(fù)的數(shù)據(jù)。例如,當(dāng)多個(gè)線程同時(shí)檢查某個(gè)鍵是否存在,并且發(fā)現(xiàn)該鍵不存在時(shí),它們可能會同時(shí)插入相同的鍵值對,導(dǎo)致數(shù)據(jù)重復(fù)。
  • 死鎖:在某些復(fù)雜的操作中,如擴(kuò)容時(shí),HashMap可能會出現(xiàn)死鎖問題。擴(kuò)容操作需要重新計(jì)算所有鍵的哈希值,并將它們重新分配到新的桶數(shù)組中。如果多個(gè)線程同時(shí)進(jìn)行擴(kuò)容操作,可能會導(dǎo)致死鎖,從而影響程序的正常運(yùn)行。

2.2 ConcurrentHashMap的線程安全性

與HashMap不同,ConcurrentHashMap是線程安全的,它通過多種機(jī)制來保證在多線程環(huán)境下的安全性。以下是ConcurrentHashMap實(shí)現(xiàn)線程安全性的主要機(jī)制:

  • 分段鎖(Segment):在早期的版本中,ConcurrentHashMap使用分段鎖來實(shí)現(xiàn)線程安全。它將整個(gè)哈希表分為多個(gè)段(Segment),每個(gè)段相當(dāng)于一個(gè)小的哈希表,并且每個(gè)段都有自己的鎖。當(dāng)對ConcurrentHashMap進(jìn)行操作時(shí),只需要鎖定相關(guān)的段,而不需要鎖定整個(gè)哈希表。這樣可以顯著提高并發(fā)性能,因?yàn)槎鄠€(gè)線程可以同時(shí)對不同的段進(jìn)行操作,而不會相互干擾。
  • CAS操作:ConcurrentHashMap使用了Compare-And-Swap(CAS)操作來保證某些操作的原子性。CAS是一種無鎖的非阻塞算法,它通過比較內(nèi)存中的值與預(yù)期值是否相等,如果相等,則將內(nèi)存中的值更新為新值。例如,在插入鍵值對時(shí),ConcurrentHashMap會使用CAS操作來更新節(jié)點(diǎn)的引用,從而保證插入操作的原子性。
  • 鎖分離:ConcurrentHashMap將讀操作和寫操作分開處理,讀操作不需要加鎖,而寫操作則需要加鎖。這種鎖分離機(jī)制可以提高并發(fā)性能,因?yàn)槎鄠€(gè)線程可以同時(shí)進(jìn)行讀操作,而不會受到寫操作的影響。只有當(dāng)進(jìn)行寫操作時(shí),才會對相關(guān)部分加鎖,從而保證數(shù)據(jù)的一致性和安全性。

2.3 線程安全性對性能的影響

線程安全性對性能有著直接的影響。對于HashMap來說,由于其非線程安全的特性,在單線程環(huán)境下可以提供較高的性能,因?yàn)椴恍枰M(jìn)行額外的鎖操作和同步處理。然而,在多線程環(huán)境下,HashMap的性能會受到嚴(yán)重影響,因?yàn)樾枰~外的同步機(jī)制來保證線程安全,如使用Collections.synchronizedMap方法對HashMap進(jìn)行包裝,或者在使用HashMap時(shí)手動進(jìn)行同步處理。

相比之下,ConcurrentHashMap由于其線程安全的特性,在多線程環(huán)境下可以提供較高的性能。它通過分段鎖、CAS操作和鎖分離等機(jī)制,減少了鎖的粒度和鎖的爭用,從而提高了并發(fā)性能。在多線程環(huán)境下,多個(gè)線程可以同時(shí)對ConcurrentHashMap進(jìn)行讀寫操作,而不會出現(xiàn)嚴(yán)重的性能瓶頸。當(dāng)然,在單線程環(huán)境下,ConcurrentHashMap的性能可能會略低于HashMap,因?yàn)槠鋬?nèi)部的線程安全機(jī)制會帶來一定的開銷。

3. 性能比較

3.1 時(shí)間復(fù)雜度

從時(shí)間復(fù)雜度的角度來看,HashMap和ConcurrentHashMap在大多數(shù)操作上的時(shí)間復(fù)雜度都是O(1),即常數(shù)時(shí)間復(fù)雜度。這是因?yàn)樗鼈兌际腔诠1韺?shí)現(xiàn)的,通過計(jì)算鍵的哈希值來快速定位對應(yīng)的桶(bucket),從而實(shí)現(xiàn)快速的插入、刪除和查找操作。

然而,在某些情況下,時(shí)間復(fù)雜度可能會退化到O(n),即線性時(shí)間復(fù)雜度。例如,當(dāng)哈希表中出現(xiàn)大量哈希沖突時(shí),即多個(gè)鍵的哈希值相同或相近,導(dǎo)致它們被分配到同一個(gè)桶中,此時(shí)需要遍歷桶中的鏈表或紅黑樹來找到對應(yīng)的鍵值對,時(shí)間復(fù)雜度會退化到O(n)。不過,這種情況在正常情況下是較少出現(xiàn)的,因?yàn)榱己玫墓:瘮?shù)和合理的擴(kuò)容機(jī)制可以有效地減少哈希沖突的發(fā)生。

3.2 并發(fā)性能

在并發(fā)性能方面,ConcurrentHashMap顯然優(yōu)于HashMap。如前所述,ConcurrentHashMap通過分段鎖、CAS操作和鎖分離等機(jī)制,減少了鎖的粒度和鎖的爭用,從而提高了并發(fā)性能。在多線程環(huán)境下,多個(gè)線程可以同時(shí)對ConcurrentHashMap進(jìn)行讀寫操作,而不會出現(xiàn)嚴(yán)重的性能瓶頸。

相比之下,HashMap在多線程環(huán)境下需要額外的同步機(jī)制來保證線程安全,這會大大降低其并發(fā)性能。例如,使用Collections.synchronizedMap方法對HashMap進(jìn)行包裝時(shí),會對所有的操作進(jìn)行同步處理,導(dǎo)致多個(gè)線程在操作HashMap時(shí)需要排隊(duì)等待,從而出現(xiàn)嚴(yán)重的性能瓶頸。

3.3 內(nèi)存占用

從內(nèi)存占用的角度來看,ConcurrentHashMap通常會比HashMap占用更多的內(nèi)存。這是因?yàn)镃oncurrentHashMap為了實(shí)現(xiàn)線程安全,需要額外的數(shù)據(jù)結(jié)構(gòu)和鎖機(jī)制。例如,在早期的版本中,ConcurrentHashMap使用分段鎖時(shí),每個(gè)段都需要占用一定的內(nèi)存空間。此外,ConcurrentHashMap在擴(kuò)容時(shí)也需要進(jìn)行更多的內(nèi)存分配和數(shù)據(jù)遷移操作,從而增加了內(nèi)存的占用。

相比之下,HashMap的內(nèi)存占用相對較少,因?yàn)樗恍枰~外的鎖機(jī)制和復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。不過,隨著ConcurrentHashMap的不斷優(yōu)化,其內(nèi)存占用情況也在逐漸改善。例如,在Java 8及以后的版本中,ConcurrentHashMap采用了新的數(shù)據(jù)結(jié)構(gòu)和優(yōu)化算法,減少了內(nèi)存的占用。

4. 使用場景

4.1 HashMap的使用場景

HashMap適用于單線程環(huán)境下的數(shù)據(jù)存儲和查詢場景。由于其非線程安全的特性,在單線程環(huán)境下可以提供較高的性能,且使用起來相對簡單。以下是一些典型的使用場景:

  • 數(shù)據(jù)緩存:在單線程應(yīng)用中,可以使用HashMap來緩存一些數(shù)據(jù),如數(shù)據(jù)庫查詢結(jié)果、計(jì)算結(jié)果等,以提高程序的執(zhí)行效率。
  • 對象映射:在對象之間建立映射關(guān)系時(shí),可以使用HashMap來存儲對象的鍵值對。例如,在一個(gè)游戲中,可以使用HashMap來存儲玩家的ID和對應(yīng)的玩家對象之間的映射關(guān)系。
  • 參數(shù)傳遞:在方法調(diào)用時(shí),可以使用HashMap來傳遞一組參數(shù),每個(gè)參數(shù)的名稱作為鍵,參數(shù)的值作為值。這種方式可以簡化參數(shù)的傳遞過程,使代碼更加靈活和可讀。

4.2 ConcurrentHashMap的使用場景

ConcurrentHashMap適用于多線程環(huán)境下的數(shù)據(jù)存儲和查詢場景。由于其線程安全的特性,在多線程環(huán)境下可以提供較高的性能和數(shù)據(jù)一致性保證。以下是一些典型的使用場景:

  • 共享數(shù)據(jù)存儲:在多線程應(yīng)用中,可以使用ConcurrentHashMap來存儲多個(gè)線程需要共享的數(shù)據(jù)。例如,在一個(gè)Web應(yīng)用中,可以使用ConcurrentHashMap來存儲用戶的會話信息,多個(gè)線程可以同時(shí)訪問和修改這些會話信息,而不會出現(xiàn)數(shù)據(jù)不一致的問題。
  • 緩存實(shí)現(xiàn):在需要實(shí)現(xiàn)線程安全的緩存時(shí),可以使用ConcurrentHashMap作為底層數(shù)據(jù)結(jié)構(gòu)。例如,在一個(gè)分布式系統(tǒng)中,可以使用ConcurrentHashMap來緩存一些共享資源的狀態(tài)信息,多個(gè)節(jié)點(diǎn)可以同時(shí)訪問和更新這些狀態(tài)信息。
  • 并發(fā)統(tǒng)計(jì):在進(jìn)行并發(fā)統(tǒng)計(jì)時(shí),可以使用ConcurrentHashMap來存儲統(tǒng)計(jì)結(jié)果。例如,在一個(gè)日志分析系統(tǒng)中,可以使用ConcurrentHashMap來統(tǒng)計(jì)不同日志級別的出現(xiàn)次數(shù),多個(gè)線程可以同時(shí)對日志進(jìn)行分析和統(tǒng)計(jì),而不會出現(xiàn)統(tǒng)計(jì)結(jié)果不準(zhǔn)確的問題。

5. 總結(jié)

在實(shí)際開發(fā)中,我們需要根據(jù)具體的使用場景和需求來選擇合適的Map實(shí)現(xiàn)類。如果是在單線程環(huán)境下,且需要處理可能存在null值的數(shù)據(jù),可以選擇使用HashMap;如果是在多線程環(huán)境下,需要保證數(shù)據(jù)的線程安全性和一致性,可以選擇使用ConcurrentHashMap。此外,還可以根據(jù)性能要求、內(nèi)存占用等因素來綜合考慮,以選擇最適合的Map實(shí)現(xiàn)類來滿足實(shí)際需求。

到此這篇關(guān)于Java中的HashMap和ConcurrentHashMap區(qū)別和適用場景的文章就介紹到這了,更多相關(guān)HashMap和ConcurrentHashMap的區(qū)別內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot使用Mybatis-Generator配置過程詳解

    SpringBoot使用Mybatis-Generator配置過程詳解

    這篇文章主要介紹了SpringBoot使用Mybatis-Generator配置過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • Spring Bean的生命周期詳細(xì)介紹

    Spring Bean的生命周期詳細(xì)介紹

    這篇文章主要介紹了Spring Bean的生命周期的相關(guān)資料,需要的朋友可以參考下
    2016-09-09
  • Java類的加載時(shí)機(jī)

    Java類的加載時(shí)機(jī)

    這篇文章介紹了Java類的加載時(shí)機(jī),文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12
  • 基于SpringBoot+JWT 實(shí)現(xiàn)Token登錄認(rèn)證與登錄人信息查詢功能

    基于SpringBoot+JWT 實(shí)現(xiàn)Token登錄認(rèn)證與登錄人信息查詢功能

    本文給大家介紹基于SpringBoot+JWT實(shí)現(xiàn)Token登錄認(rèn)證與登錄人信息查詢功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值
    2026-03-03
  • Java實(shí)現(xiàn)簡單掃雷程序

    Java實(shí)現(xiàn)簡單掃雷程序

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡單掃雷程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • Java?Stream函數(shù)式編程管道流結(jié)果處理

    Java?Stream函數(shù)式編程管道流結(jié)果處理

    這篇文章主要為大家介紹了Java?Stream函數(shù)式編程管道流結(jié)果處理的示例過程解析需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-03-03
  • springboot跨域CORS處理代碼解析

    springboot跨域CORS處理代碼解析

    這篇文章主要介紹了springboot跨域CORS處理代碼解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • Java后端Tomcat實(shí)現(xiàn)WebSocket實(shí)例教程

    Java后端Tomcat實(shí)現(xiàn)WebSocket實(shí)例教程

    WebSocket protocol 是HTML5一種新的協(xié)議。它實(shí)現(xiàn)了瀏覽器與服務(wù)器全雙工通信(full-duplex)。一開始的握手需要借助HTTP請求完成握手。本文給大家介紹Java后端Tomcat實(shí)現(xiàn)WebSocket實(shí)例教程,感興趣的朋友一起學(xué)習(xí)吧
    2016-05-05
  • Java通俗易懂講解泛型

    Java通俗易懂講解泛型

    在正式進(jìn)入內(nèi)容之前說明一下:泛型的內(nèi)容太多,也太復(fù)雜。這里因?yàn)镴ava中寫數(shù)據(jù)結(jié)構(gòu)的時(shí)候會使用到,所以加上。關(guān)于泛型我找了挺多文章,再結(jié)合自己的理解,盡可能將其講清楚。不求會使用泛型,只要求后面數(shù)據(jù)結(jié)構(gòu)出現(xiàn)泛型的時(shí)候能夠知道是在干什么即可
    2022-05-05
  • springboot1.X和2.X中如何解決Bean名字相同時(shí)覆蓋

    springboot1.X和2.X中如何解決Bean名字相同時(shí)覆蓋

    這篇文章主要介紹了springboot1.X和2.X中如何解決Bean名字相同時(shí)覆蓋,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03

最新評論

赞皇县| 昔阳县| 北京市| 阳谷县| 盐城市| 盐城市| 新乡县| 秭归县| 秭归县| 加查县| 信阳市| 林口县| 吴旗县| 铅山县| 白河县| 通州市| 横山县| 江山市| 岳池县| 土默特左旗| 宽城| 西乡县| 隆子县| 皮山县| 泰来县| 松溪县| 保靖县| 蕉岭县| 蓝山县| 阳西县| 额尔古纳市| 临泉县| 广饶县| 海阳市| 雅安市| 拜泉县| 龙山县| 阿拉尔市| 南乐县| 离岛区| 荣昌县|