簡體   English   中英

為什么 Map.of 不允許空鍵和值?

[英]Why does Map.of not allow null keys and values?

在 Java 9 中,為ListSetMap接口引入了新的工廠方法。 這些方法允許使用一行中的值快速實例化 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鍵和值 [...]。 嘗試插入不合格的鍵或值會引發未經檢查的異常,通常為NullPointerExceptionClassCastException

...和收集工廠(不僅僅是在地圖上) 利用了那個

它們不允許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。

在我看來,不可空性對鍵有意義,但對值則不然。

  1. Map接口變成了一個弱契約,你不能在不查看實現的情況下相信它的行為,那就是假設你有權查看它。 Java11 的Map.of()不允許空值,而HashMap允許,但它們都實現了相同的Map契約 - 為什么?
  2. 有一個空值或根本沒有值是不可能的,並且可以說不應該被視為一個不同的場景。 當你得到一個鍵的值並且你得到空值時,誰在乎它是否明確地放在那里? 提供的鍵根本沒有值,地圖不包含這樣的鍵,就這么簡單。 Null 為 null,沒有 nullnull 或 null^2
  3. 現有的庫廣泛使用 map 作為向它們傳遞屬性的一種手段,其中許多是可選的,這使得Map.of()作為此類結構的構造毫無用處。
  4. Kotlin 在編譯時強制執行可空性,並允許使用沒有已知問題的空值映射。
  5. 不允許空值背后的原因可能只是由於創建者的實現細節和便利性。

主要區別在於:當您以“選項 1”的方式構建自己的 Map 時……那么您就含蓄地說:“我希望在我正在做的事情上擁有完全的自由”。

因此,當您決定您的地圖應該有一個空鍵或值(可能是出於此處列出的原因)時,您可以自由地這樣做。

但是“選項 2”是關於方便的事情 - 可能旨在用於常量。 Java 背后的人簡單地決定:“當你使用這些方便的方法時,結果映射應該是空的”。

允許空意味着

 if (map.contains(key)) 

不一樣

 if (map.get(key) != null)

有時這可能是一個問題。 或者更准確地說:這是在處理那個地圖對象時要記住的事情。

這只是一個軼事提示,為什么這似乎是一種合理的方法:我們的團隊自己實現了類似的便利方法。 猜猜看:在對未來 Java 標准方法的計划一無所知的情況下 - 我們的方法做完全相同的事情:它們返回傳入數據的不可變副本; 當您提供 null 元素時,它們會拋出。 我們甚至非常嚴格,當您傳入列表/地圖/...時,我們也會抱怨。

在地圖中允許空值是一個錯誤。 我們現在可以看到它,但我想不清楚什么時候引入HashMap NullpointerException是生產代碼中最常見的錯誤

我會說 JDK 朝着幫助開發人員對抗 NPE 瘟疫的方向發展。 一些例子:

  • Optional介紹
  • Collectors.toMap(keyMapper, valueMapper)不允許keyMappervalueMapper函數返回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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM