Le JavaDoc de ConcurrentHashMap dit ceci:
Comme
Hashtable
mais contrairement àHashMap
, cette classe ne pas permet ànull
d'être utilisée comme clé ou valeur.
Ma question: pourquoi?
2ème question: pourquoi Hashtable n'autorise-t-il pas null?
J'ai utilisé beaucoup de HashMaps pour stocker des données. Mais lors du passage à ConcurrentHashMap, j'ai eu plusieurs fois des ennuis à cause de NullPointerExceptions.
De l'auteur de ConcurrentHashMap
lui-même (Doug Lea) :
La principale raison pour laquelle les valeurs NULL ne sont pas autorisées dans ConcurrentMaps (ConcurrentHashMaps, ConcurrentSkipListMaps) est que les ambiguïtés qui peuvent être à peine tolérables dans les cartes non simultanées ne peuvent pas être prises en compte. Le principal est que si
map.get(key)
renvoienull
, vous ne pouvez pas détecter si la clé est explicitement mappée ànull
par rapport à la clé qui n'est pas mappée. Dans une mappe non simultanée, vous pouvez vérifier cela viamap.contains(key)
, mais dans une mappe simultanée, la mappe peut avoir changé entre les appels.
Je crois que c'est, au moins en partie, pour vous permettre de combiner containsKey
et get
en un seul appel. Si la carte peut contenir des valeurs NULL, il n'y a aucun moyen de savoir si get
renvoie une valeur NULL car il n'y avait pas de clé pour cette valeur, ou simplement parce que la valeur était NULL.
Pourquoi c'est un problème? Parce qu'il n'y a aucun moyen sûr de le faire vous-même. Prenez le code suivant:
if (m.containsKey(k)) {
return m.get(k);
} else {
throw new KeyNotPresentException();
}
Étant donné que m
est une mappe simultanée, la clé k peut être supprimée entre les appels containsKey
et get
, ce qui renvoie cet extrait de code qui n'a jamais été dans la table, plutôt que le KeyNotPresentException
souhaité.
Normalement, vous pouvez résoudre ce problème en synchronisant, mais avec une carte simultanée, cela ne fonctionnera bien sûr pas. Par conséquent, la signature de get
a dû changer, et la seule façon de le faire d'une manière rétrocompatible était d'empêcher l'utilisateur d'insérer des valeurs nulles en premier lieu et de continuer à l'utiliser comme espace réservé pour la clé " pas trouvé".
Josh Bloch a conçu HashMap
; Doug Lea a conçu ConcurrentHashMap
. J'espère que ce n'est pas diffamatoire. En fait, je pense que le problème est que les valeurs NULL nécessitent souvent un habillage pour que le vrai NULL puisse signifier non initialisé. Si le code client requiert des valeurs nulles, il peut payer le coût (certes faible) de l'encapsulation des valeurs nulles lui-même.
ConcurrentHashMap est thread-safe. Je crois que ne pas autoriser les clés et les valeurs nulles faisait partie de la vérification de la sécurité des threads.
Vous ne pouvez pas synchroniser sur un null.
Edit: ce n'est pas exactement pourquoi dans ce cas. Au début, je pensais qu'il y avait quelque chose d'extraordinaire à verrouiller les choses contre les mises à jour simultanées ou à utiliser le moniteur d'objets pour détecter si quelque chose avait été modifié, mais en examinant le code source il semble que j'avais tort - ils se verrouillent en utilisant un "segment" basé sur un masque de bits du hachage.
Dans ce cas, je soupçonne qu'ils l'ont fait pour copier Hashtable, et je pense que Hashtable l'a fait parce que dans le monde de la base de données relationnelle, null! = Null, donc l'utilisation d'un null comme clé n'a pas de sens.
Je suppose que l'extrait de code suivant de la documentation de l'API donne une bonne indication: "Cette classe est entièrement interopérable avec Hashtable dans les programmes qui reposent sur sa sécurité des threads mais pas sur ses détails de synchronisation."
Ils voulaient probablement juste rendre ConcurrentHashMap
entièrement compatible/interchangeable avec Hashtable
. Et comme Hashtable
n'autorise pas les clés et les valeurs nulles.