HashSet如何不允许重复?

时间:2014-03-06 09:49:49

标签: java hashmap hashset

我正在使用add的{​​{1}}方法。提到

  

如果此set已包含该元素,则调用将保持set不变并返回false。

HashSet方法在内部保存add

中的值
HashMap

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

  

将指定的值与此映射中的指定键相关联。如果地图以前包含该键的映射,则替换旧值。

因此,如果HashMap的{​​{1}}方法替换旧值,那么put HashMap方法如何在重复元素的情况下保持集不变?< /强>

6 个答案:

答案 0 :(得分:32)

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

现在,OLD == PRESENT并不重要 - 请注意Map.put不会更改密钥,只会更改映射到该密钥的值。由于地图的Set真正关心的,因此Set不会改变。

事实上,已经Set的基础结构进行了一些更改 - 它取代了(a, OLD)(a, PRESENT)的映射。但是在Set的实施之外,这是不可观察到的。 (事实上​​,这种变化甚至不是真正的变化,因为OLD == PRESENT)。

答案 1 :(得分:8)

您可能正在寻找的答案归结为支持hashmap将集合的元素映射到HashSet.java中定义的值PRESENT,如下所示:

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实际上并没有改变地图,因此也没有改变集合!

答案 2 :(得分:5)

正如您所看到的,HashSet.add方法将元素作为键添加到HashMap.put而不是值。值将替换为HashMap而不是密钥。

答案 3 :(得分:3)

请参阅HashMap#put

  

将指定的值与此映射中的指定键相关联。如果   地图以前包含键的映射,旧值是   替换。

它用 new 值替换密钥,这样,HashSet中就不会有重复项。

答案 4 :(得分:2)

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

e是关键,因此如果 e 已经存在,put将不会返回null。因此add将返回false。

put的JavaDoc:

  

与key关联的上一个值,如果没有key的映射,则返回null。 (null返回也可以指示映射先前将null与key关联。)

答案 5 :(得分:1)

从javadocs for HashMap.put(), “将指定的值与此映射中的指定键关联。如果映射先前包含键的映射,则替换旧值。”

因此地图值将被替换,(这是HashSet类中的常量静态字段,因此替换了相同的实例),并且地图键保持不变(实际上是Set集合项)< / p>