簡體   English   中英

HashSet 如何不允許重復?

[英]How does HashSet not allow duplicates?

我正在經歷HashSetadd方法。 提到

如果此集合已包含該元素,則調用將保持該集合不變並返回 false。

但是add方法是在內部保存HashMap的值

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

HashMapput方法指出

將指定值與此映射中的指定鍵相關聯。 如果映射先前包含鍵的映射,則舊值將被替換。

那么如果HashMapput方法替換了舊值, HashSet add方法如何在出現重復元素的情況下保持集合不變呢?

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, PRESENT)替換了(a, OLD)的映射。 但這不是從Set的實現之外觀察到的。 (碰巧的是,這種變化甚至不是真正的變化,因為OLD == PRESENT )。

您可能正在尋找的答案歸結為支持哈希映射將集合的元素映射到值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 的value PRESENT 但是因為PRESENT是靜態的和最終的,所以只有一個這樣的實例; 所以賦值e.value = value實際上根本不會改變地圖,因此也不會改變集合!

更新

一旦HashSet被初始化。
- 其中的所有項目都作為鍵存儲在HashMap
- HashMap所有值只有一個PRESENT對象,它是HashSet一個靜態字段

正如你所看到的HashSet.add方法添加元素到HashMap.put作為重點而不是一個值。 值在HashMap被替換而不是鍵。

HashMap#put

將指定值與此映射中的指定鍵相關聯。 如果映射先前包含鍵的映射,則舊值將被替換。

它用值替換鍵,這樣, HashSet不會有重復項。

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

e 是鍵,所以如果e已經存在put將不會返回null 因此add將返回 false。

用於put JavaDoc:

與 key 關聯的先前值,如果沒有 key 的映射,則為 null。 (空返回也可以表明映射先前將空與鍵相關聯。)

來自 HashMap.put() 的 javadoc,“將指定值與此映射中的指定鍵相關聯。如果映射先前包含該鍵的映射,則替換舊值。”

這樣map值就會被替換,(它是HashSet類中的一個常量靜態字段,因此替換了同一個實例),而map鍵保持不變(實際上就是Set集合項)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM