web-dev-qa-db-fra.com

Comment HashSet n'autorise-t-il pas les doublons?

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 HashSetadd laisse l'ensemble inchangé en cas d'éléments en double ?

32
Zeeshan

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).

32
yshavit

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!

8
Ray Toal

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é.

5
shazin

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.

3
Maroun
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é.)

2
Batty

À 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)

1
sutanu dalui