[英]Why does Map.of not allow null keys and values?
在 Java 9 中,為List
、 Set
和Map
接口引入了新的工廠方法。 這些方法允許使用一行中的值快速實例化 Map 對象。 現在,如果我們考慮:
Map<Integer, String> map1 = new HashMap<Integer, String>(Map.of(1, "value1", 2, "value2", 3, "value3"));
map1.put(4, null);
以上是允許的,沒有任何例外,而如果我們這樣做:
Map<Integer, String> map2 = Map.of(1, "value1", 2, "value2", 3, "value3", 4, null );
它拋出:
Exception in thread "main" java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:221)
..
我無法得到,為什么在第二種情況下不允許 null 。
我知道 HashMap 可以將 null 作為鍵和值,但為什么在 Map.of 的情況下會受到限制?
同樣的事情發生在java.util.Set.of("v1", "v2", null)
和java.util.List.of("v1", "v2", null)
。
正如其他人指出的那樣, Map
合約允許拒絕空值......
[S] 某些實現禁止
null
鍵和值 [...]。 嘗試插入不合格的鍵或值會引發未經檢查的異常,通常為NullPointerException
或ClassCastException
。
...和收集工廠(不僅僅是在地圖上) 利用了那個。
它們不允許
null
鍵和值。 嘗試使用null
鍵或值創建它們會導致NullPointerException
。
在集合中允許null
現在被視為設計錯誤。 這有多種原因。 一個好的是可用性,其中最突出的麻煩制造者是Map::get
。 如果它返回null
,則不清楚是缺少鍵還是值為null
。 一般來說,保證null
集合更容易使用。 在實現方面,它們還需要較少的特殊外殼,使代碼更易於維護和性能更高。
你可以聽 Stuart Marks在這個演講中解釋它,但JEP 269 (介紹工廠方法的那個)也總結了它:
將不允許使用空元素、鍵和值。 (最近引入的集合都沒有支持空值。)此外,禁止空值為更緊湊的內部表示、更快的訪問和更少的特殊情況提供了機會。
由於HashMap
在慢慢被發現時已經被廣泛使用,因此在不破壞現有代碼的情況下對其進行更改為時已晚,但是這些接口的最新實現(例如ConcurrentHashMap
)不再允許null
並且工廠方法的新集合是也不例外。
(我認為另一個原因是顯式使用null
值被視為可能的實現錯誤,但我錯了。這將復制密鑰,這也是非法的。)
所以禁止null
有一些技術原因,但這樣做也是為了使用創建的集合提高代碼的健壯性。
確切地說 - HashMap
允許存儲 null,而不是靜態工廠方法返回的Map
。 並非所有地圖都相同。
一般來說,據我所知,首先允許HashMap
空值作為鍵是錯誤的,較新的集合禁止這種可能性開始。
想想你的HashMap
中有一個條目,它有一個特定的鍵和值 == 空的情況。 你確實得到了,它返回null
。 這是什么意思? 它有一個null
映射還是不存在?
Key
——來自這樣一個空密鑰的哈希碼必須一直被特殊處理。 禁止空值開始 - 讓這更容易。
雖然 HashMap 確實允許空值,但Map.of
不使用HashMap
並且如果將其中一個用作鍵或值,則會引發異常,如 文檔所示:
Map.of() 和 Map.ofEntries() 靜態工廠方法提供了一種創建不可變映射的便捷方法。 這些方法創建的 Map 實例具有以下特點:
...
它們不允許空鍵和值。 嘗試使用空鍵或值創建它們會導致 NullPointerException。
在我看來,不可空性對鍵有意義,但對值則不然。
Map
接口變成了一個弱契約,你不能在不查看實現的情況下相信它的行為,那就是假設你有權查看它。 Java11 的Map.of()
不允許空值,而HashMap
允許,但它們都實現了相同的Map
契約 - 為什么?Map.of()
作為此類結構的構造毫無用處。主要區別在於:當您以“選項 1”的方式構建自己的 Map 時……那么您就含蓄地說:“我希望在我正在做的事情上擁有完全的自由”。
因此,當您決定您的地圖應該有一個空鍵或值(可能是出於此處列出的原因)時,您可以自由地這樣做。
但是“選項 2”是關於方便的事情 - 可能旨在用於常量。 Java 背后的人簡單地決定:“當你使用這些方便的方法時,結果映射應該是空的”。
允許空值意味着
if (map.contains(key))
不一樣
if (map.get(key) != null)
有時這可能是一個問題。 或者更准確地說:這是在處理那個地圖對象時要記住的事情。
這只是一個軼事提示,為什么這似乎是一種合理的方法:我們的團隊自己實現了類似的便利方法。 猜猜看:在對未來 Java 標准方法的計划一無所知的情況下 - 我們的方法做完全相同的事情:它們返回傳入數據的不可變副本; 當您提供 null 元素時,它們會拋出。 我們甚至非常嚴格,當您傳入空列表/地圖/...時,我們也會抱怨。
在地圖中允許空值是一個錯誤。 我們現在可以看到它,但我想不清楚什么時候引入HashMap
。 NullpointerException
是生產代碼中最常見的錯誤。
我會說 JDK 朝着幫助開發人員對抗 NPE 瘟疫的方向發展。 一些例子:
Optional
介紹Collectors.toMap(keyMapper, valueMapper)
不允許keyMapper
和valueMapper
函數返回null
值null
Stream.findFirst()
和Stream.findAny()
將拋出 NPE 所以,我認為在新的 JDK9 不可變集合(和映射)中禁止null
朝着相同的方向發展。
文檔沒有說明為什么不允許使用null
:
它們不允許
null
鍵和值。 嘗試使用null
鍵或值創建它們會導致NullPointerException
。
在我看來,將Map.of()
和Map.ofEntries()
靜態工廠方法主要由編譯類型的開發人員形成。 那么,保留null
作為鍵或值的原因是什么?
而Map#put
通常用於在運行時填充可能出現null
鍵/值的映射。
並非所有 Map 都允許 null 作為鍵
docs of Map
提到的原因。
一些地圖實現對它們可能包含的鍵和值有限制。 例如,有些實現禁止空鍵和值,有些實現對其鍵的類型有限制。 嘗試插入不合格的鍵或值會引發未經檢查的異常,通常為 NullPointerException 或 ClassCastException。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.