Je passais par la méthode add
de HashSet
. Il est mentionné que
Si cet ensemble contient déjà l'élément, l'appel laisse l'ensemble inchangé et renvoie faux.
Mais la méthode add
enregistre en interne les valeurs dans HashMap
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
La méthode put
de HashMap
indique que
Associe la valeur spécifiée à la clé spécifiée dans cette carte. Si la carte contenait précédemment un mappage pour la clé, l'ancienne valeur est remplacée.
Donc, si la méthode put
de HashMap
remplace l'ancienne valeur, comment la méthode HashSet
add
laisse l'ensemble inchangé en cas d'éléments en double ?
PRESENT
n'est qu'une valeur fictive - l'ensemble ne se soucie pas vraiment de ce qu'il est. Ce dont se soucie l'ensemble , ce sont les touches de la carte . Donc la logique va comme ceci:
Set.add(a):
map.put(a, PRESENT) // so far, this is just what you said
the key "a" is in the map, so...
keep the "a" key, but map its value to the PRESENT we just passed in
also, return the old value (which we'll call OLD)
look at the return value: it's OLD, != null. So return false.
Maintenant, le fait que OLD == PRESENT
n'a pas d'importance - et notez que Map.put
ne change pas la clé, juste la valeur mappée à cette clé. Étant donné que les touches de la carte sont ce à quoi le Set
tient vraiment, le Set
est inchangé.
En fait, certains changements ont été apportés aux structures sous-jacentes du Set
- il a remplacé un mappage de (a, OLD)
avec (a, PRESENT)
. Mais ce n'est pas observable de l'extérieur de l'implémentation de Set
. (Et en l'occurrence, ce changement n'est même pas un vrai changement, puisque OLD == PRESENT
).
La réponse que vous cherchez peut se résumer au fait que le hashmap de support mappe les éléments de l'ensemble à la valeur PRESENT
qui est définie dans HashSet.Java comme suit:
private static final Object PRESENT = new Object();
Dans le code source de HashMap.put
on a:
386 public V put(K key, V value) {
387 if (key == null)
388 return putForNullKey(value);
389 int hash = hash(key.hashCode());
390 int i = indexFor(hash, table.length);
391 for (Entry<K,V> e = table[i]; e != null; e = e.next) {
392 Object k;
393 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
394 V oldValue = e.value;
395 e.value = value;
396 e.recordAccess(this);
397 return oldValue;
398 }
399 }
400
401 modCount++;
402 addEntry(hash, key, value, i);
403 return null;
404 }
Étant donné que la clé en question existe déjà, nous prendrons le retour anticipé à la ligne 397. Mais vous pensez peut-être qu'une modification est apportée à la carte de la ligne 395, dans laquelle il semble que nous modifions la valeur d'une entrée de carte. Cependant, la valeur de value
est PRESENT
. Mais parce que PRESET
est statique et final, il n'y a qu'une seule instance de ce type; et donc l'affectation e.value = value
en fait ne change pas du tout la carte, et donc l'ensemble!
Comme vous pouvez voir le HashSet.add
la méthode ajoute l'élément au HashMap.put
comme clé et non comme valeur. La valeur est remplacée dans HashMap
et non dans la clé.
Voir HashMap#put
:
Associe la valeur spécifiée à la clé spécifiée dans cette carte. Si la carte contenait précédemment un mappage pour la clé, l'ancienne valeur est remplacée.
Il remplace la clé par la nouvelle valeur , de cette façon, aucun doublon ne sera dans le HashSet
.
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
e est la clé, donc si e est déjà présent put
ne retournera pas null
. Par conséquent, add
renverra false.
JavaDoc pour put
:
la valeur précédente associée à la clé, ou null s'il n'y avait pas de mappage pour la clé. (Un retour nul peut également indiquer que la carte a précédemment associé null à la clé.)
À partir de javadocs pour HashMap.put (), "Associe la valeur spécifiée à la clé spécifiée dans cette carte. Si la carte contenait précédemment un mappage pour la clé, l'ancienne valeur est remplacée."
Ainsi, la valeur de la carte sera remplacée, (qui est un champ statique constant dans la classe HashSet, et donc la même instance est remplacée), et la clé de la carte reste intacte (qui, en fait IS l'élément de collection Set)