Java - 为什么Map.put()会覆盖原有值而Set.add()不会?

25

我想知道为什么Java的Map.put(key, value)方法会覆盖已经存在于集合中的键相等的值,而Set.add(value)不会覆盖已经存在于集合中的等效值?

编辑:

看起来大多数人认为,在集合中评估为相等的对象应在所有方面都相等,因此Set.add(Object)是否覆盖了等效值的对象并不重要。如果两个对象在评估上相等,但实际上持有不同的数据,则Map类型的集合是更合适的容器。

我有些不同意这种观点。
例如:一个保存"Person"对象组的集合。为了更新关于该人的一些信息,您可能希望向集合传递一个新的、更新的人对象来覆盖旧的、过时的人对象。在这种情况下,Person将持有一个标识该个体的主键,集合将仅根据它们的主键识别和比较人物。这个主键是人的一部分身份,而不是类似于Map所暗示的外部引用。


4
为什么 Set.add(value) 会覆盖?如果这是期望的行为,那么你实现 equals/hash 的方式就有问题,因为只有当两个对象具有完全相同的属性时它们才应该被视为相等(在这种情况下,无论哪一个在集合中都无关紧要)。但 Put 必须覆盖 value,否则怎样才能获得给定键的不同值呢? - Bill K
我之前在下面谈到了如何覆盖.equals()和.hashCode()方法,只查看对象的主键(如果有)。然而,这个线程上普遍的观点似乎是,这种类型的数据最好用Map类型的集合来表示。 - Chris Dutrow
这不是你可以“反对”的“观点”,而是java.util.Set的记录行为。你当然可以创建自己的数据结构来实现你所描述的功能,但如果你期望现有代码按照你想要的方式运行,而不是按照设计的方式运行,那么你会遇到麻烦的。 - dimo414
4个回答

10
Map行为可以更改与等效键相关联的值。 这是一个非常常见的用例:a:b变成a:c
是的,使用add覆盖Set内容可能会改变某些东西(引用值)-但这似乎是一个非常狭窄的用例(无论如何都可以实现-始终尝试在添加之前删除:s.remove(o);s.add(o);),相对于大多数情况下获得的内容-不循环什么。
编辑:
我能想到那种行为的一个潜在用途是具有受限内存预算,在各个地方创建许多重复的重型但等效对象,并且具有对不同版本的引用,从而防止垃圾收集器收集重复的对象。 但是,遇到过这个问题后,我认为这种行为甚至不是解决它的最佳方法。

5

我认为在Set中覆盖某个元素是没有意义的,因为它不会造成任何改变。

但是当你更新一个Map时,键可能相同,但值却可能不同。


2
根据你实现set的方式有所不同。在我的情况下,我使用了键值对象。我实现了comparable并重写了.equals()和.hashcode()方法,使得set只查看对象上的主键。当向set中添加一个元素时,我希望它能用持有新信息的对象覆盖已经拥有该关键字的任何对象。但是,当这种情况没有发生时,我感到惊讶。 - Chris Dutrow
5
听起来你确实有一个映射表:主键 -> 对象 - Michael Brewer-Davis
1
我起初使用了一个映射表。但我遇到的问题是在映射表中重复键和对象,这可能导致数据不一致的问题。虽然将键从对象中移出并仅在映射表中保留它也许可行,但这可能会在其他地方造成问题。 - Chris Dutrow
1
@DutrowLLC - 在一个映射中不应该存在重复的键,这会破坏映射的目的。 - Alexandru Luchian
@DutrowLLC,没有什么能阻止你使用map.put(object.key, object)。只要确保键不会改变(如果哈希码发生变化,你的映射可能会失去对它的追踪)。 - aioobe
显示剩余2条评论

1
请注意,Map并不是真的有很大的区别...它可能会始终更改值,但是(至少在Sun的实现中),即使后续对put()的调用使用与原始实例相等的不同实例,键仍将保持不变。

0

我不同意你问题的前提。Map和Set都是抽象接口。它们是否覆盖是实现细节。

  1. 不覆盖的Map实现
  2. 你可以创建一个可变的单例集合 - 将东西添加到集合中会覆盖现有的单例值。

1
你是否真正阅读过java.util.Set的javadocs,特别是add方法的文档? - President James K. Polk
Set的javadocs明确表明它旨在模拟数学概念中的集合。它实际上所做的是由实现类指定的。编译器不强制执行javadocs注释。 - emory
3
如果您对Java有所了解,就会知道该语言没有表达能力来提供接口的所有合同要求的执行。尽管如此,您可以假设实现Set(或List或其他任何内容)的所有类都遵守合同。所有Sun类都是如此,如果您实现Set并且不遵守规定,您的代码实际上会着火并烧毁您。 - President James K. Polk

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