Java HashSet 如何不允许重复?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22220692/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does HashSet not allow duplicates?
提问by Zeeshan
I was going through the add
method of HashSet
. It is mentioned that
我正经过add
的方法HashSet
。提到
If this set already contains the element, the call leaves the set unchanged and returns false.
如果此集合已包含该元素,则调用将保持该集合不变并返回 false。
But the add
method is internally saving the values in HashMap
但是该add
方法在内部将值保存在HashMap
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
The put
method of HashMap
states that
的put
方法HashMap
是
Associates the specified value with the specified key in this map. If the map previously contained a mapping for the key, the old value is replaced.
将指定值与此映射中的指定键相关联。如果映射先前包含键的映射,则旧值将被替换。
So if the put
method of HashMap
replaces the old value, how the HashSet
add
method leaves the set unchanged in case of duplicate elements?
因此,如果该put
方法HashMap
取代了旧的价值,如何将HashSet
add
法叶集不变的重复元素的情况下?
采纳答案by yshavit
PRESENT
is just a dummy value -- the set doesn't really care what it is. What the set doescare about is the map's keys. So the logic goes like this:
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.
Now, the fact that OLD == PRESENT
doesn't matter -- and note that Map.put
doesn't change the key, just the value mapped to that key. Since the map's keysare what the Set
really cares about, the Set
is unchanged.
现在,事实OLD == PRESENT
并不重要——请注意,Map.put
不会更改键,只是映射到该键的值。由于地图的键是Set
真正关心的,所以Set
是不变的。
In fact, there hasbeen some change to the underlying structures of the Set
-- it replaced a mapping of (a, OLD)
with (a, PRESENT)
. But that's not observable from outside the Set
's implementation. (And as it happens, that change isn't even a real change, since OLD == PRESENT
).
事实上,的底层结构已经发生了一些变化Set
——它取代了(a, OLD)
with的映射(a, PRESENT)
。但这不是从Set
的实现之外观察到的。(碰巧的是,这种变化甚至不是真正的变化,因为OLD == PRESENT
)。
回答by shazin
As you can see the HashSet.add
method adds the element to the HashMap.put
as a key not as a value. Value is replaced in the HashMap
not the key.
如您所见,该HashSet.add
方法将元素HashMap.put
作为键而不是值添加到。值在HashMap
非键中被替换。
回答by Maroun
See HashMap#put
:
Associates the specified value with the specified key in this map. If the map previously contained a mapping for the key, the old value is replaced.
将指定值与此映射中的指定键相关联。如果映射先前包含键的映射,则旧值将被替换。
It replaces the key with the newvalue, this way, no duplicates will be in the HashSet
.
它用新值替换键,这样,HashSet
.
回答by Batty
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
e is the key, So if eis already present put
will not return null
. Hence add
will return false.
e 是键,所以如果e已经存在put
则不会返回null
。因此add
将返回false。
JavaDoc for put
:
JavaDoc 用于put
:
the previous value associated with key, or null if there was no mapping for key. (A null return can also indicate that the map previously associated null with key.)
与 key 关联的先前值,如果没有 key 的映射,则为 null。(空返回也可以表明映射先前将空与键相关联。)
回答by Ray Toal
The answer that you may be looking comes down to the fact that the backing hashmap maps the elements of the set to the value PRESENT
which is defined in HashSet.javaas follows:
您可能正在寻找的答案归结为支持哈希映射将集合的元素映射到HashSet.java 中PRESENT
定义的值的事实,如下所示:
private static final Object PRESENT = new Object();
In the source code for HashMap.put
we have:
在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 }
Because the key in question already exists, we will take the early return on line 397. But you might think a change is being made to the map on line 395, in which it appears that we are changing the value of a map entry. However, the value of value
is PRESENT
. But because PRESENT
is static and final, so there is only one such instance; and so the assignment e.value = value
actually doesn't change the map, and therefore the set, at all!
因为有问题的键已经存在,我们将在第 397 行获取早期返回值。但您可能认为正在对第 395 行的映射进行更改,在该行中,我们似乎正在更改映射条目的值。然而,价值value
为PRESENT
。但是因为PRESENT
是static和final,所以只有一个这样的实例;所以这个任务e.value = value
实际上根本不会改变地图,因此也不会改变集合!
Update:
Once a
HashSet
is initialized.
- All the items in it are stored as keys in aHashMap
- All the values thatHashMap
have ONLY ONE object that isPRESENT
which is a static field inHashSet
更新:
一旦 a
HashSet
被初始化。
- 其中的所有项目都作为键存储在 aHashMap
- 所有HashMap
只有一个对象的值,PRESENT
它是一个静态字段HashSet
回答by sutanu dalui
From javadocs for HashMap.put(), "Associates the specified value with the specified key in this map. If the map previously contained a mapping for the key, the old value is replaced."
来自 HashMap.put() 的 javadoc,“将指定值与此映射中的指定键相关联。如果映射先前包含该键的映射,则替换旧值。”
Thus the map value will be replaced, (which is a constant static field in HashSet class, and thus the same instance is replaced), and the map key is kept untouched (which, in fact IS the Set collection item)
这样map值就会被替换,(它是HashSet类中的一个常量静态字段,因此替换了同一个实例),而map键保持不变(实际上就是Set集合项)