web-dev-qa-db-fra.com

Pourquoi Map.of n'autorise-t-il pas les clés et les valeurs nulles?

Avec Java 9, de nouvelles méthodes d'usine ont été introduites pour les interfaces List, Set et Map. Ces méthodes permettent d'instancier rapidement une carte objet avec des valeurs sur une ligne. Maintenant, si nous considérons:

Map<Integer, String> map1 = new HashMap<Integer, String>(Map.of(1, "value1", 2, "value2", 3, "value3"));
map1.put(4, null);

Ce qui précède est autorisé sans exception alors que si nous le faisons:

Map<Integer, String> map2 = Map.of(1, "value1", 2, "value2", 3, "value3", 4, null );

Il jette:

Exception in thread "main" Java.lang.NullPointerException
    at Java.base/Java.util.Objects.requireNonNull(Objects.Java:221)
..

Je ne parviens pas à obtenir pourquoi la valeur null n'est pas autorisée dans le deuxième cas.

Je sais que HashMap peut prendre null comme clé ainsi que comme valeur, mais pourquoi cela a-t-il été limité dans le cas de Map.of?

La même chose se produit dans le cas de Java.util.Set.of("v1", "v2", null) et Java.util.List.of("v1", "v2", null).

57
hi.nitish

Comme d'autres l'ont fait remarquer, le Map contrat permet de rejeter les valeurs nulles ...

[Quelques] implémentations interdisent null clés et valeurs [...]. Tenter d’insérer une clé ou une valeur non éligible lève une exception non contrôlée, généralement NullPointerException ou ClassCastException.

... et les usines de collecte (pas seulement sur les cartes) tilisez-le .

Ils interdisent null clés et valeurs. Les tentatives pour les créer avec les clés ou les valeurs null donnent NullPointerException.

Mais pourquoi?

Autoriser null dans les collections est maintenant considéré comme une erreur de conception. Cela a une variété de raisons. Un bon exemple est la convivialité, où le fauteur de troubles le plus important est Map::get. S'il renvoie null, il est difficile de savoir si la clé est manquante ou si la valeur était null. De manière générale, les collections garanties null free sont plus faciles à utiliser. Du point de vue de la mise en œuvre, ils requièrent également des boîtiers moins spéciaux, ce qui rend le code plus facile à gérer et plus performant.

Vous pouvez écouter Stuart Marks l'expliquer dans cet exposé mais JEP 269 (celui qui a introduit les méthodes d'usine) le résume également:

Les éléments nuls, les clés et les valeurs ne seront pas autorisés. (Aucune collection récemment introduite ne prend en charge les valeurs NULL.) En outre, l'interdiction des valeurs NULL offre des possibilités de représentation interne plus compacte, d'accès plus rapide et de moins de cas spéciaux.

Puisque HashMap était déjà dans la nature lorsque cela a été découvert lentement, il était trop tard pour le changer sans rompre le code existant mais les implémentations les plus récentes de ces interfaces (par exemple, ConcurrentHashMap ) n'autorise plus null et les nouvelles collections pour les méthodes d'usine ne font pas exception.

(Je pensais qu'une autre raison était que l'utilisation explicite de null valeurs était considérée comme une erreur d'implémentation probable, mais je me suis trompé. Cela impliquait de dupliquer des clés, qui sont également illégales.)

Donc, le refus de null avait une raison technique, mais cela a également été fait pour améliorer la robustesse du code en utilisant les collections créées.

64
Nicolai

Exactement - un HashMap est autorisé à stocker null, pas le Map renvoyé par les méthodes de fabrique statique. Pas tous les cartes sont les mêmes.

En règle générale, autant que je sache, les erreurs les plus récentes dans la clé HashMap constituent une erreur, mais les nouvelles collections interdisent cette possibilité.

Pensez au cas où vous avez une entrée dans votre HashMap qui a une certaine clé et une certaine valeur == null. Vous obtenez, il retourne null. Qu'est-ce que cela signifie? Il a un mappage de null ou il n'est pas présent?

Il en va de même pour un Key - un hashcode provenant d'une telle clé nulle doit être traité de manière spécifique tout le temps. Interdire les valeurs NULL pour commencer - facilite les choses.

10
Eugene

Bien que HashMap autorise les valeurs NULL, Map.of n'utilise pas HashMap et lève une exception si elle est utilisée comme clé ou comme valeur, comme documenté :

Les méthodes d'usine statiques Map.of () et Map.ofEntries () constituent un moyen pratique de créer des cartes immuables. Les instances de carte créées par ces méthodes présentent les caractéristiques suivantes:

  • ...

  • Ils interdisent les clés et les valeurs nulles. Les tentatives pour les créer avec des clés ou des valeurs NULL entraînent NullPointerException.

8
1615903

Autoriser les valeurs NULL dans les cartes a été une erreur. Nous pouvons le voir maintenant , mais je suppose que ce n'était pas clair quand HashMap a été introduit. NullpointerException est le bogue le plus courant observé dans le code de production.

Je pourrais dire que le JDK va dans le sens d'aider les développeurs à lutter contre le fléau des NPE. Quelques exemples:

  • Introduction de Optional
  • Collectors.toMap(keyMapper, valueMapper) n'autorise ni la fonction keyMapper ni la fonction valueMapper à renvoyer une valeur null
  • Stream.findFirst() et Stream.findAny() jettent NPE si la valeur trouvée est null

Donc, refuser null dans les nouvelles collections immuables JDK9 (et la carte) va dans la même direction. Et nous devrions tous en être remerciés!

La principale différence est la suivante: lorsque vous créez votre propre carte, la méthode "option 1" ... vous êtes implicitement en disant: "Je veux avoir liberté dans ce que je fais ".

Ainsi, lorsque vous décidez que votre carte doit avoir une clé ou une valeur nulle (peut-être pour les raisons énumérées ici ), vous êtes libre de le faire.

Mais "option 2" est une question de commodité - probablement destinée à être utilisée pour les constantes. Et les personnes derrière Java ont simplement décidé: "lorsque vous utilisez ces méthodes pratiques, la carte résultante est alors sans valeur nulle".

Permettre des valeurs nulles signifie que

 if (map.contains(key)) 

n'est pas la même chose que

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

ce qui peut être un problème parfois. Ou plus précisément: il faut se souvenir de cet objet cartographique.

Et juste une indication anecdotique de la raison pour laquelle cela semble être une approche raisonnable: notre équipe a elle-même mis en œuvre des méthodes de commodité similaires. Et devinez quoi: sans rien savoir des projets d’avenir Java - nos méthodes procèdent exactement de la même manière: elles renvoient une copie immuable des données entrantes; Nous sommes même si stricts que lorsque vous passez des listes vides /cartes/... nous nous plaignons également.

5
GhostCat

Toutes les cartes n'autorisent pas la nullité en tant que clé

La raison mentionnée dans le docs of Map .

Certaines implémentations de carte ont des restrictions sur les clés et les valeurs qu'elles peuvent contenir. Par exemple, certaines implémentations interdisent les clés et les valeurs nulles, et certaines ont des restrictions sur les types de leurs clés. Tenter d'insérer une clé ou une valeur non éligible lève une exception non contrôlée, généralement NullPointerException ou ClassCastException.

3
Suresh Atta

La documentation ne dit pas pourquoinull n'est pas autorisé:

Ils interdisent null clés et valeurs. Les tentatives pour les créer avec les clés ou les valeurs null aboutissent à NullPointerException.

À mon avis, les méthodes fabriques statiques Map.of() et Map.ofEntries(), qui vont produire une constante, sont principalement formées par un développeur du type compilé. Alors, quelle est la raison de garder un null comme clé ou valeur?

Tandis que le Map#put est généralement utilisé pour remplir une carte au moment de l'exécution où null clés/valeurs peuvent apparaître.

2
Andrew Tobilko