欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入淺析HashMap key和value能否為null

 更新時間:2025年04月08日 16:57:40   作者:fixAllenSun  
HashMap的key和value可為null,線程不安全,HashTable的key和value均不可為null,線程安全,ConcurrentHashMap在多線程場景下使用,key和value也不能為null,還對它們進(jìn)行了測試和底層代碼分析,本文介紹HashMap key和value能否為null,感興趣的朋友跟隨小編一起看看吧

【一】HashMap

(1)結(jié)論:HashMap對象的key、value值均可為null
HashMap 的 key 和 value 都可以為 null 值。在 Java 中,HashMap 允許 null 作為 key 和 value 的值。當(dāng)插入 null 作為 key 時,它將被存儲在 HashMap 的第一個位置上(即桶數(shù)組的第一個位置),而當(dāng)插入 null 作為 value 時,它可以存儲在任何一個位置上。當(dāng)然,需要注意的是,由于 HashMap 是根據(jù) key 的哈希值來確定存儲位置的,所以插入 null 值作為 key 時需要格外小心,以避免出現(xiàn)哈希沖突導(dǎo)致的問題。

(2)key能否重復(fù)
key值不能重復(fù),若添加key相同的鍵值對,后面的value會自動覆蓋前面的value,但不會報錯

(3)當(dāng)key為空時,key的hash值為0,所以如果再設(shè)置一個值會對原有value進(jìn)行覆蓋

(4)HashMap是線程不安全的,他的key和value都可以為null

HashMap求hash值時,并不是一上來就直接用key值求,他先進(jìn)行了一個判斷,如果為null,hash值為0。

對于get()方法

返回的是null,此時null值不知道是未找到還是對應(yīng)的value值。
這就出現(xiàn)了一個問題:當(dāng)A線程使用containsKey()進(jìn)行判斷時,發(fā)現(xiàn)有這個元素,當(dāng)他調(diào)用get()取這個元素時,B線程加入了進(jìn)來,B線程將這個元素移除掉了,此時A線程取得的值為null,A線程會以為自己取到了這個值,但實際上此時的null是未找到的null。這樣線程間就有可能出現(xiàn)安全問題。
以至于我們在多線程情況下,使用的是currentHashMap存儲數(shù)據(jù),它的key和balue都是不能為null的。

【二】HashTable

(1)結(jié)論:HashTable對象的key、value值均不可為null
HashTable是線程安全的,HashTable對象的key、value值均不可為null。

當(dāng)我們調(diào)用put()方法時:

為什么要一來就判斷value值不能為null呢?這就要看到get()方法:

發(fā)現(xiàn)沒有,如果value值能為null,那么我傳入對應(yīng)的key值,他找到了返回的是value值,也就是null,當(dāng)找不到時,他也返回的是null。找到找不到返回值都是null,這怎么分辨?

所以,HashTable的key和value值都不能為null。

【三】ConcurrentHashMap

結(jié)論:key和value都不能為null

假定ConcurrentHashMap也可以存放value為null的值。那不管是HashMap還是ConcurrentHashMap調(diào)用map.get(key)的時候,如果返回了null,那么這個null,都有兩重含義:
(1)這個key從來沒有在map中映射過。
(2)這個key的value在設(shè)置的時候,就是null。

但是hashmap可以通過 containskey來確定到底是哪一個原因!

而多線程情況下,ConcurrentHashMap中的value不能為null

原因如下:
ConcurrentHashMap的使用場景為多線程。用反證法來推理,假設(shè)concurrentHashMap允許存放值為null的value。這時有A、B兩個線程。線程A調(diào)用concurrentHashMap.get(key)方法,返回為null,我們還是不知道這個null是沒有映射的null還是存的值就是null。

我們假設(shè)此時返回為null的真實情況就是因為這個key沒有在map里面映射過。那么我們可以用concurrentHashMap.containsKey(key)來驗證我們的假設(shè)是否成立,我們期望的結(jié)果是返回false。

但是在我們調(diào)用concurrentHashMap.get(key)方法之后,containsKey方法之前,有一個線程B執(zhí)行了concurrentHashMap.put(key,null)的操作。那么我們調(diào)用containsKey方法返回的就是true了。這就與我們的假設(shè)的真實情況不符合了。也就是上面說的二義性。

上面也說了,hashmap可以key為null,但可以存在多個null嗎?

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

hashmap方法里面,當(dāng)k==null返回0,所以只要key為null就將Node插入到索引為0的桶當(dāng)中,那下一個null來了怎么辦?源碼當(dāng)中寫了,首先判斷這個存在的節(jié)點,如果它們的hashcode相等,下一步判斷key是否相同,這里判斷用到了||,就是地址一樣(都是null也成了),或者equals相同也可以,就進(jìn)行替換,所以得出結(jié)論:hashmap 當(dāng)中 key為null的只有一個?。?!

【四】測試代碼

public class Test {
	public static void main(String[] args) {
		Map<String, String> map = new HashMap<String, String>();//HashMap對象
		Map<String, String> tableMap = new Hashtable<String, String>();//HashTable對象
		map.put(null, null);
		System.out.println("hashMap的[key]和[value]均可以為null:" + map.get(null));
		try {
			tableMap.put(null, "3");
			System.out.println(tableMap.get(null));
		} catch (Exception e) {
			System.out.println("【ERROR】:hashTable的[key]不能為null");
		}
		try {
			tableMap.put("3", null);
			System.out.println(tableMap.get("3"));
		} catch (Exception e) {
			System.out.println("【ERROR】:hashTable的[value]不能為null");
		}
	}
}
import java.util.HashMap;
import java.util.Hashtable;
public class TestMap {
    public static void main(String[] args){
        HashMap<Integer,Integer> map = new HashMap<>();
        System.out.println(map.containsKey(null));
        System.out.println(map.get(null));
        //驗證 HashMap的key和value都可以為null
        //當(dāng)key為空時,key的hash值為0
        map.put(null,null);
        System.out.println(map.containsKey(null));
        System.out.println(map.get(null));
        //當(dāng)key為空時,key的hash值為0,所以如果再設(shè)置一個值會對原有value進(jìn)行覆蓋
        map.put(null,123);
        System.out.println(map.containsKey(null));
        System.out.println(map.get(null));
        //驗證 Hashtable
        Hashtable<Integer,Integer> hashtable = new Hashtable<>();
        System.out.println(hashtable.containsKey(null));
        System.out.println(hashtable.get(null));
        //HashTable是線程安全的,key和value都不可以為null
        //HashMap是線程不安全的,他的key和value都可以為null
        //驗證 HashMap的key和value都可以為null
        //當(dāng)key為空時,key的hash值為0
        hashtable.put(null,null);
        System.out.println(hashtable.containsKey(null));
        System.out.println(hashtable.get(null));
    }
}

執(zhí)行效果

false
null
true
null
true
123
Exception in thread "main" java.lang.NullPointerException
    at java.util.Hashtable.containsKey(Hashtable.java:336)
    at com.itheima.test.TestMap.main(TestMap.java:31)

【五】底層代碼分析

【1】Hashtable

    public synchronized V put(K key, V value) {
        // 確保value不為空。這句代碼過濾掉了所有value為null的鍵值對。因此Hashtable不能
        // 存儲value為null的鍵值對
        if (value == null) {
            throw new NullPointerException();
        }
        // 確保key在table數(shù)組中尚未存在。
        Entry<?,?> tab[] = table;
        int hash = key.hashCode(); //在此處計算key的hash值,如果此處key為null,則直接拋出空指針異常。
        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的源碼可以看出,如果value=null直接拋出空指針異常;而使用key.hashCode()不允許key=null,所以無論是key還是value都不能是null。而在HashMap中并沒有這樣的限制,key和value允許使用null。

【2】HashMap

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

HashMap計算key的hash值時調(diào)用單獨的方法,在該方法中會判斷key是否為null,如果是則返回0;

到此這篇關(guān)于深入淺析HashMap key和value能否為null的文章就介紹到這了,更多相關(guān)HashMap key和value能否為null內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 基于JavaMail的Java實現(xiàn)簡單郵件發(fā)送功能

    基于JavaMail的Java實現(xiàn)簡單郵件發(fā)送功能

    這篇文章主要為大家詳細(xì)介紹了基于JavaMail的Java實現(xiàn)簡單郵件發(fā)送功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-09-09
  • Springboot項目啟動不加載resources目錄下的文件問題

    Springboot項目啟動不加載resources目錄下的文件問題

    這篇文章主要介紹了Springboot項目啟動不加載resources目錄下的文件問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Spring?Boot項目抵御XSS攻擊實戰(zhàn)過程

    Spring?Boot項目抵御XSS攻擊實戰(zhàn)過程

    XSS攻擊又稱跨站腳本攻擊,通常指利用網(wǎng)頁開發(fā)時留下的漏洞,通過巧妙的方法注入惡意指令代碼到網(wǎng)頁,使用戶加載并執(zhí)行攻擊者惡意制造的網(wǎng)頁程序,下面這篇文章主要給大家介紹了關(guān)于Spring?Boot項目抵御XSS攻擊的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • Java8加java10等于Java18的版本查看及特性詳解

    Java8加java10等于Java18的版本查看及特性詳解

    這篇文章主要為大家介紹了Java?8加java10等于Java18的各個版本要點詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 淺談java線程join方法使用方法

    淺談java線程join方法使用方法

    這篇文章主要介紹了淺談java線程join方法使用方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Java中的指令重排詳解

    Java中的指令重排詳解

    在 Java 中,指令重排是一種性能優(yōu)化技術(shù),它涉及到編譯器和處理器對程序中指令的執(zhí)行順序進(jìn)行調(diào)整,以提高執(zhí)行效率,本文給大家詳細(xì)介紹了Java中的指令重排,需要的朋友可以參考下
    2023-12-12
  • JAVA十大排序算法之堆排序詳解

    JAVA十大排序算法之堆排序詳解

    這篇文章主要介紹了java中的冒泡排序,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考
    2021-08-08
  • springboot多模塊多環(huán)境配置文件問題(動態(tài)配置生產(chǎn)和開發(fā)環(huán)境)

    springboot多模塊多環(huán)境配置文件問題(動態(tài)配置生產(chǎn)和開發(fā)環(huán)境)

    這篇文章主要介紹了springboot多模塊多環(huán)境配置文件問題(動態(tài)配置生產(chǎn)和開發(fā)環(huán)境),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • springboot配置文件中使用${}注入值的兩種方式小結(jié)

    springboot配置文件中使用${}注入值的兩種方式小結(jié)

    這篇文章主要介紹了springboot配置文件中使用${}注入值的兩種方式小結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 超實用的Java快捷鍵(總結(jié))

    超實用的Java快捷鍵(總結(jié))

    下面小編就為大家?guī)硪黄瑢嵱玫腏ava快捷鍵(總結(jié))。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05

最新評論