Объект Null в реализации HashSet

В Java API реализация HashSet использует объект как значение для внутри HashMap,

   // Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

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

но HashMap позволяет его значение равно нулю. Я думаю, что не нужно заполнять значение, так зачем это нужно?

Ответ 1

Поскольку контракт HashSet указывает, что remove() возвращает true, если указанный объект существует и был удален. Для этого используется обернутый HashMap#remove(), который возвращает удаленное значение.

Если вы хотите хранить null вместо объекта, то вызов HashMap#remove() будет возвращать null, который был бы неотличим от результата попытки удалить несуществующий объект, а контракт HashSet.remove() не может быть выполнено.

Ответ 2

но HashMap позволяет его значение null

Почему это важно, когда значение полностью контролируется HashSet? Это гарантирует, что единственным значением, когда-либо связанным с ключом, является PRESENT. Поэтому, если map.put возвращает null, это может быть только потому, что ранее не было записи для этого ключа.

Значение только там, потому что нужно указать какое-то значение, и если значение было указано как null, это было бы плохо - было бы сложнее сказать, было ли значение перед вызовом add. Если вы укажете любое ненулевое значение, вы можете также заставить его быть одним и тем же значением все время - вы бы не хотели, чтобы он задерживал сборку мусора, например.

Теперь, если вы спрашиваете, почему HashSet реализуется с точки зрения HashMap, а не является более эффективной реализацией, которая вообще не записывает значение, это другой вопрос, и у меня нет ответ.

Ответ 3

В Java HashMap сопоставление от объекта к null не совпадает с тем, что объект вообще не присутствует на карте. Рассмотрим:

Object exists = new Object();
map.put(exists, null);
System.out.println(map.contains(exists)) // "true"
System.out.println(map.get(exists)) // "null"
Object notMapped = new Object();
System.out.println(map.contains(notMapped)) // "false"
System.out.println(map.get(notMapped)) // "null"

Кроме того, HashMap.put() возвращает старое значение с помощью введенного вами ключа, который в вашем случае равен null (либо потому, что этот ключ не был на карте, либо его значение было нулевым).

Ответ 4

С помощью Map, если вы вызываете put(key, null), вы не можете определить разницу между

  • уже существовал ключ, сопоставление с null
  • для этого ключа не было сопоставления

Так как HashSet add делегирует HashMap.put, PRESENT требуется выполнить контракт Set.add, который возвращает false, если объект уже существует в Set:

return map.put(e, PRESENT)==null; 

Ответ 5

обратите внимание на часть ==null...............