Подтвердить что ты не робот

Как HashSet не позволяет дублировать?

Я прошел через add метод HashSet. Отмечено, что

Если этот набор уже содержит элемент, вызов оставляет неизменным и возвращает false.

Но метод add внутренне сохраняет значения в HashMap

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

В методе put HashMap указано, что

Связывает указанное значение с указанным ключом на этой карте. Если ранее карта содержала отображение для ключа, старое значение заменяется.

Итак, если метод put HashMap заменяет старое значение, как метод HashSet add оставляет набор неизменным в случае повторяющихся элементов?

4b9b3361

Ответ 1

PRESENT - это просто фиктивное значение - на самом деле набор не имеет значения. Что касается набора, это ключи карты. Итак, логика выглядит так:

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 OLD, != null. So return false.

Теперь тот факт, что OLD == PRESENT не имеет значения - и обратите внимание, что Map.put не изменяет ключ, просто значение, сопоставленное этому ключу. Так как клавиши карты - это то, что действительно волнует Set, Set не изменяется.

Фактически, произошли некоторые изменения в базовых структурах Set - он заменил отображение (a, OLD) на (a, PRESENT). Но это не видно извне реализации Set. (И как это бывает, это изменение даже не является реальным изменением, так как OLD == PRESENT).

Ответ 2

Ответ, который вы, возможно, смотрите, сводится к тому, что хэш-карта поддержки отображает элементы набора в значение PRESENT, которое определено в HashSet.java следующим образом:

private static final Object PRESENT = new Object();

В исходном коде для HashMap.put мы имеем:

  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     }

Поскольку этот ключ уже существует, мы берем раннее возвращение в строке 397. Но вы можете подумать, что происходит изменение карты на строке 395, в которой, как представляется, мы меняем значение карты запись. Однако значение value равно PRESENT. Но поскольку PRESET является статическим и окончательным, есть только один такой экземпляр; и поэтому присваивание e.value = value фактически не изменяет карту и, следовательно, множество вообще!

Ответ 3

Как вы видите, метод HashSet.add добавляет элемент в HashMap.put в качестве ключа не как значение. Значение заменяется на HashMap не на ключ.

Ответ 4

См. HashMap#put:

Связывает указанное значение с указанным ключом на этой карте. Если на карте ранее содержалось отображение для ключа, старое значение заменить.

Он заменяет ключ новым значением, таким образом, никакие дубликаты не будут в HashSet.

Ответ 5

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

e - это ключ, поэтому, если e уже присутствует put не вернется null. Следовательно, add вернет false.

JavaDoc для put:

предыдущее значение, связанное с ключом, или null, если не было сопоставления для ключа. (Нулевой возврат также может указывать на то, что ранее связанная карта null с ключом.)

Ответ 6

Из javadocs для HashMap.put(), "Связывает указанное значение с указанным ключом на этой карте. Если ранее карта содержала отображение для ключа, старое значение заменяется."

Таким образом, значение карты будет заменено (это постоянное статическое поле в классе HashSet и, следовательно, тот же экземпляр заменяется), а ключ карты остается нетронутым (что фактически является элементом Set collection)