将重复的值添加到HashSet/HashMap中是否会替换先前的值?

159

请考虑下面的代码:

HashSet hs = new HashSet();
hs.add("hi"); -- (1)
hs.add("hi"); -- (2)

hs.size()会返回1,因为HashSet不允许有重复元素,所以只会存储一个元素。

我想知道如果添加了重复的元素,它会替换先前的元素还是不添加它?

另外,如果使用HashMap做同样的操作会发生什么?

9个回答

278

HashMap 的情况下,它会用新值替换旧值。

而在 HashSet 的情况下,该项不会被插入。


1
不确定我缺少什么,但是[源代码](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/HashSet.java#HashSet.add%28java.lang.Object%29)似乎表明不同? 我看到他们没有在支持的HashMap上进行检查,以查看在调用支持的map上的put之前是否已存在key - mystarrocks
11
关键在于Set的元素,这个元素永远不会被put()操作替换。 - Keppil
1
啊,我现在明白了。我理解了Set的关键是元素,但刚刚意识到put()只会覆盖值,而不是键。在这种情况下,它将再次与键一起放置相同的值,这可能比检查键是否存在并放置更好,也可能不是。无论哪种方式,我都明白它是如何工作的。 - mystarrocks
2
只是好奇,为什么HashMap和HashSet选择这样做? - Helin Wang
非常正确。在这里查看代码:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/HashSet.java#HashSet.add%28java.lang.Object%29 - roottraveller
显示剩余2条评论

59
你需要知道的第一件事是,HashSet 行为像一个 Set,这意味着你直接将对象添加到 HashSet 中,它不能包含重复项。你只需直接在 HashSet 中添加你的值。
然而,HashMap 是一种 Map 类型。这意味着每次添加一个条目时,你都会添加一个键-值对。
HashMap 中,你可以有重复的值,但不能有重复的键。在 HashMap 中,新条目将替换旧条目。最近的条目将出现在 HashMap 中。
了解 HashMap 和 HashSet 之间的联系:
记住,HashMap 不能有重复的键。在幕后,HashSet 使用一个 HashMap
当你尝试将任何对象添加到 HashSet 中时,此条目实际上存储为 HashMap 中的一个键 - 这个与 HashSet 幕后使用的相同的 HashMap。由于这个底层的 HashMap 需要一个键-值对,因此生成了一个虚拟值。
现在,当你尝试将另一个重复对象插入到同一个 HashSet 中时,它会再次尝试将其作为键插入到下面的 HashMap 中。然而,HashMap 不支持重复项。因此,HashSet 仍将只有一个该类型的值。顺便说一句,对于每个重复的键,由于在 HashSet 中为我们的条目生成的值是一些随机/虚拟值,因此根本不会替换键。它将被忽略,因为删除键并重新添加相同的键(虚拟值相同)根本没有任何意义。
总结: HashMap 允许重复的 ,但不允许重复的 HashSet 不能包含重复项。

为了检查添加对象是否成功完成,您可以检查调用.add()时返回的布尔值,并查看它是否返回truefalse。如果它返回true,则已插入。


HashMap允许重复的值,但会用新值替换旧值。 - Alex78191

24

文档非常清晰: HashSet.add 不会 替换:

如果该集合中没有该元素,则将指定的元素添加到该集合中。更正式地说,如果此集合不包含任何元素 e2,使得 (e==null ? e2==null : e.equals(e2)),则将指定的元素 e 添加到该集合中。 如果该集合已经包含该元素,则调用不会改变集合并返回 false。

但是HashMap.put 替换:

如果该映射以前包含键的映射,则旧值将被替换。


4
在HashSet的情况下,它不会替换它。
来自文档:

http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html#add(E)

如果该集合中不存在指定元素,则将其添加到该集合中。更正式地说,如果该集合不包含任何元素e2使得(e==null ? e2==null : e.equals(e2)),则将指定元素e添加到该集合中。如果该集合已经包含该元素,则调用不会改变集合并返回false。

2

如果我理解得不对,请纠正我,但你的意思是说,在字符串中,“Hi”==“Hi”并不总是为真(因为它们不一定是同一个对象)。

然而,你之所以得到1的答案是因为JVM会尽可能地重用字符串对象。在这种情况下,JVM正在重用字符串对象,从而覆盖了Hashmap / Hashset中的项目。

但是,你不能保证这种行为(因为它可能是具有相同值“Hi”的不同字符串对象)。你看到的行为只是由于JVM的优化。


1

HashMap基本上包含Entry,其中又包含Key(Object)Value(Object)。内部上HashSetHashMap,并且HashMap确实会替换一些值,但它真正替换的是键吗?不是,这就是诀窍。HashMap在底层HashMap中将其值作为键保留,而该值仅仅是一个虚拟对象。因此,如果您尝试重新插入相同的值到HashMap(基础Map的键),它只会替换虚拟值而不是HashSet的键(底层Map的值)。

看下面的HashSet Class代码:

public boolean  [More ...] add(E e) {

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

e 是 HashSet 的值,但是它是底层 map 的键。而且键值从未被替换。希望我已经澄清了混淆。


0

你需要先检查哈希映射中的put方法,因为HashSet由HashMap支持

  1. 当你向HashSet中添加重复值,比如字符串"One"
  2. 一个条目("one", PRESENT)将被插入到Hashmap中(对于添加到集合中的所有值,值都是"PRESENT",它是Object类型)
  3. Hashmap向Map中添加该条目并返回该值,在这种情况下为"PRESENT",如果不存在该条目则为null。
  4. 如果Hashmap返回的值等于null,则HashSet的add方法返回true,否则返回false,这意味着该条目已经存在...

0
换句话说:当您将一个键值对插入到 HashMap 中且该键已存在(在某种意义上,hashvalue() 给出相同的值并且 equal() 为 true,但两个对象仍然可以有多种不同之处)时,键不会被替换,但值会被覆盖。键只是用于获取 hashvalue() 并使用它在表中查找值。 由于 HashSet 使用 HashMap 的键,并设置任意值(对用户而言并不重要),因此集合的元素也不会被替换。

0
简单来说,如果你查看HashSet的add方法,你会发现它有一个可选的返回类型,是布尔值True/False。当HashSet无法添加元素时,返回false是有意义的。

你能举个例子吗? - Erik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接