java之map集合存放null值的問(wèn)題
java map集合存放null值問(wèn)題
今天接到阿里的電話面試提到了一個(gè)問(wèn)題是關(guān)于HashTable、HashMap的區(qū)別
| 集合類 | key | value | super | 說(shuō)明 |
| HashTable | 不能為null | 不能為null | Dictionary | 線程安全 |
| ConcurrentHashMap | 不能為null | 不能為null | AbstractMap | 線程局部安全 |
| TreeMap | 不能為null | 可以為null | AbstractMap | 線程不安全 |
| HashMap | 可以為null | 可以為null | AbstractMap | 線程不安全 |
以此為記~
java 集合能否加入null值
項(xiàng)目場(chǎng)景
劍指 Offer 09. 用兩個(gè)棧實(shí)現(xiàn)隊(duì)列

public int deleteHead() {
while(s2.isEmpty()){
//注意這里,我直接判斷第二個(gè)??站图尤氲谝粋€(gè)棧中的元素,沒(méi)有判斷第一個(gè)棧是否為空(當(dāng)時(shí)想的是第一個(gè)棧為空不會(huì)加入元素或者拋出異常)
s2.offerFirst(s1.pollFirst());
}
if(s1.isEmpty()&&s2.isEmpty()){
return -1;
}
return s2.pollFirst();
}
問(wèn)題描述
寫力扣題時(shí)出現(xiàn)了這個(gè)問(wèn)題,原代碼在上面。

原因分析
嘗試輸出了下,發(fā)現(xiàn)總是返回空值,
1)認(rèn)為輸入棧(s1)為空會(huì)拋出異常,但是忘記了offer和poll針對(duì)異常不做處理,push和pop會(huì)拋出異常

2)跟著debug發(fā)現(xiàn)輸出棧(s2)元素永遠(yuǎn)不為空,因?yàn)榧尤肓藄1的null,這就有意思了,java中有的集合可以加入null,有的不可以,所以順便總結(jié)一下。
解決方案
public int deleteHead() {
while(s1.isEmpty()&&s2.isEmpty()){
return -1;
}
if(s2.isEmpty()){
while(!s1.isEmpty()){
s2.offerFirst(s1.pollFirst());
}
}
return s2.pollFirst();
}
1.List
可以看到ArrayList可以存儲(chǔ)多個(gè)null,ArrayList底層是數(shù)組,添加null并未對(duì)他的數(shù)據(jù)結(jié)構(gòu)造成影響。LinkedList底層為雙向鏈表,node.value = null也沒(méi)有影響。
@Test
public void testArrayList() {
ArrayList<Integer> list = new ArrayList<>();
list.add(null);
list.add(null);
System.out.println(list.size());//2
}
@Test
public void testLinkedList() {
LinkedList<Integer> list = new LinkedList<>();
list.add(null);
list.add(null);
System.out.println(list.size());//2
}
2.Set
Set底層是map,所以和map一樣
3.Map
關(guān)于Map,HashMap中最多只有一個(gè)key == null的節(jié)點(diǎn),因?yàn)閗ey相同時(shí),后面的節(jié)點(diǎn)會(huì)替換之前相同key的節(jié)點(diǎn),所以HashMap是可以添加key == null 的節(jié)點(diǎn)的,只不過(guò)只會(huì)存在一個(gè)put方法返回舊值。
TreeMap的put方法會(huì)調(diào)用compareTo方法,對(duì)象為null時(shí),會(huì)報(bào)空指針錯(cuò)。
@Test
public void testHashMap() {
HashMap<Integer, Integer> map = new HashMap<>();
map.put(null, null);
map.put(null, null);
System.out.println(map.size());//1
}
@Test
public void testTreeMap() {
TreeMap<Integer, Integer> map = new TreeMap<>();
map.put(null, null);
System.out.println(map.size());//NullPointerException
}
TreeMap源碼

4.Vector
Vector 底層是數(shù)組,所以不會(huì)管你元素的內(nèi)容是什么,可以存儲(chǔ)多個(gè)null
@Test
public void VectorTest(){
Vector box = new Vector();
box.add(null);
box.add(null);
Assert.assertEquals(2,box.size()); //ok
}
/**
*此為Vector的add函數(shù)
**/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
5.HashTable
HashTable底層為散列表,無(wú)論是key為null,還是value為null,都會(huì)報(bào)錯(cuò)
@Test
public void HashTableTest(){
Hashtable table = new Hashtable();
//table.put(new Object(),null); //Exception
//table.put(null,new Object()); //Exception
//table.put(null,null); //Exception
Assert.assertEquals(1,table.size());
}
//此為hashTable#put函數(shù)源碼
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) { //value 需要判空,所以value不可為null
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode(); //key需要擁有實(shí)例去調(diào)用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鍵和值都設(shè)計(jì)成不能為null,牛客網(wǎng)的一位大佬這樣說(shuō)到:

6.ConcurrentHashMap
看上面那張圖
總結(jié)
結(jié)果:
- List都可以添加null元素
- HashMap可以有1個(gè)key為null的元素,TreeMap不能key為null的元素
- Set底層是Map,所以HashSet可以有1個(gè)null的元素,TreeSet不能有key為null的元素。
- -線程安全的類key和value都不能是null
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java基礎(chǔ)MAC系統(tǒng)下IDEA連接MYSQL數(shù)據(jù)庫(kù)JDBC過(guò)程
最近一直在學(xué)習(xí)web項(xiàng)目,當(dāng)然也會(huì)涉及與數(shù)據(jù)庫(kù)的連接這塊,這里就總結(jié)一下在IDEA中如何進(jìn)行MySQL數(shù)據(jù)庫(kù)的連接,這里提一下我的電腦是MAC系統(tǒng),使用的編碼軟件是IDEA,數(shù)據(jù)庫(kù)是MySQL2021-09-09
在Linux環(huán)境下搭建Java環(huán)境和部署Web項(xiàng)目
本文介紹了在Linux環(huán)境下搭建Java部署環(huán)境的步驟,包括使用虛擬機(jī)、WSL、云服務(wù)器等方法,重點(diǎn)講解了使用云服務(wù)器(如阿里云)的詳細(xì)步驟,包括購(gòu)買服務(wù)器、連接服務(wù)器、配置遠(yuǎn)程登錄、安裝必要的軟件包(如JDK和MySQL)以及部署Web項(xiàng)目2026-03-03
springboot自定義yml配置文件及其外部部署過(guò)程
這篇文章主要介紹了springboot自定義yml配置文件及其外部部署過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Java中使用Preconditions來(lái)檢查傳入?yún)?shù)介紹
這篇文章主要介紹了Java中使用Preconditions來(lái)檢查傳入?yún)?shù)介紹,本文只是作為一個(gè)簡(jiǎn)單的用法介紹,需要的朋友可以參考下2015-06-06
Spring中@Async注解實(shí)現(xiàn)異步調(diào)詳解
在本篇文章里小編給大家分享的是關(guān)于Spring中@Async注解實(shí)現(xiàn)異步調(diào)詳解內(nèi)容,需要的朋友們可以學(xué)習(xí)下。2020-04-04
springboot統(tǒng)一接口返回?cái)?shù)據(jù)的實(shí)現(xiàn)
這篇文章主要介紹了springboot統(tǒng)一接口返回?cái)?shù)據(jù)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
Mybatis-plus多數(shù)據(jù)源配置的兩種方式總結(jié)
這篇文章主要為大家詳細(xì)介紹了Mybatis-plus中多數(shù)據(jù)源配置的兩種方式,文中的示例代碼簡(jiǎn)潔易懂,感興趣的小伙伴可以跟隨小編一起了解一下2022-10-10
Java中JSON對(duì)象字段為null值的顯示處理方法
這篇文章主要給大家介紹了關(guān)于Java中JSON對(duì)象字段為null值的顯示處理方法,最近開發(fā)過(guò)程中前端反應(yīng)后臺(tái)返回的json中包含null,不好處理,這里介紹下,需要的朋友可以參考下2023-08-08

