当前位置: 首页 > 面试题库 >

对象Hashcode更改时,在Hashmap或Hashset中查找会发生什么情况

曹铭晨
2023-03-14
问题内容

在哈希图中,提供的键的哈希码用于将值放在哈希表中。在哈希集中,对象哈希码用于将值放置在基础哈希表中。即,哈希图的优点是您可以灵活地确定要作为密钥的内容,这样您就可以完成类似的事情。

Map<String,Player> players = new HashMap<String,Player>();

这可以将诸如玩家名称之类的字符串映射到玩家本身。

我的问题是,当键的哈希码更改时,查找会发生什么情况。

我希望这不是Hashmap的主要关注点,因为我既不希望也不希望更改密钥。在前面的示例中,如果玩家名称更改,那么他将不再是该玩家。但是,我可以使用密钥更改来查找玩家,但其他字段不是名称,将来的查找将可用。

但是,在哈希集中,因为如果有人稍微更改对象,整个对象的哈希码将用于放置项目,则该对象的将来查找将不再解析到哈希表中的相同位置,因为它依赖于整个对象哈希码。这是否意味着一旦将数据放入哈希集中,就不应对其进行更改。还是需要重新修复?还是自动完成等?到底是怎么回事?


问题答案:

在您的示例中,字符串是不可变的,因此其哈希码无法更改。但是假设,如果某个对象的哈希码确实是一个哈希表中的键而发生了变化,那么就哈希表查找而言,它很
可能会消失
。我在一个相关问题的答案中对此做了更详细的介绍:。(最初的问题是关于a
HashSet,但a HashSet实际上是一个HashMap底线,因此答案也涵盖了这种情况。)


为了补充@EJP的答案,如果您将a中的对象突变HashSet以使其重复(在equals/hashcode合同意义上),那么在实践中将会发生的事情是哈希表数据结构将中断。

根据突变的确切细节以及哈希表的状态,一个或两个实例将对查找(例如contains和其他操作)不可见。它要么在错误的哈希链上,要么是因为另一个实例出现在哈希链之前。而且很难预测哪个实例将是可见的……以及它是否仍将保持可见。

如果您对集合进行迭代,则这两个实例仍将存在…违反Set合同。

当然,从应用程序的角度来看,这是非常糟糕的。

您可以通过以下任一方法避免此问题:

为您的设置元素使用不可变的类型,
在将对象放入集合中和/或从集合中拉出对象时进行复制,
编写代码,使其“知道”在持续时间内不更改对象…
从正确性和鲁棒性的角度来看,第一种选择显然是最好的。

顺便说一句,以一般方式“解决”这一问题确实很困难。Java中没有普遍的机制来知道……或被告知……某些元素已更改。您可以在逐个类的基础上实现这种机制,但必须对其进行显式编码(而且价格不菲)。即使您确实有这种机制,您会怎么做?显然,现在应该从集合中删除其中一个对象…但是,哪个对象呢?

它是安全地说,如果任何一个HashMap或映像树的钥匙,影响他们各自的方式突变hashcode()/
equals(Object)compare(...)compareTo(...)合同,然后将数据结构“破发”。

这是否意味着一旦将数据放入哈希集中,就不应对其进行更改。

是。

还是需要重新修复?还是自动完成等?

它不会自动修复。该HashMap不会注意到一个关键的哈希码已经改变。确实,HashMap调整大小后,您甚至都不会重新计算哈希码。数据结构会 记住
原始哈希码值,以避免在哈希表调整大小时必须重新计算所有哈希码。

如果您知道某个键的哈希码将要更改,则需要在对该键进行突变之前将其从表中删除,然后再添加回去。(如果您在更改密钥后尝试remove/
put,则很可能remove会找不到该条目。)

到底是怎么回事?

发生的情况是您违反了HashMapjavadocs中明确规定的合同。不要那样做!



 类似资料:
  • 问题内容: 几分钟前,我回答了一个问题,询问“ Java中HashMap的最大可能大小 ”。正如我一直阅读的那样, HashMap是一个可扩展的数据结构。 它的大小仅受JVM内存大小的限制。 因此,我认为它的大小没有硬性限制,因此做出了相应的回答。(同样适用于HashSet。) 但有人纠正我说,既然 大小() HashMap中的方法返回一个 INT ,还有 就是 它的大小有限制。完全正确的一点。我

  • 如果你看看hashMap里面java的hashCode方法,你会发现: 因此,当您将内容插入哈希映射时,哈希映射的哈希代码将更改。因此,如果我们在哈希集中插入一个空的哈希映射,然后向此哈希映射插入一些内容,然后调用 它将返回 。为什么 Java 允许这样的行为?这很容易导致哈希集中出现重复的项目。 请尝试运行以下代码:

  • 问题内容: 我的老师给我一个问题: “用Java创建对象时会发生什么”。 据我所知,创建对象时会发生内存分配,变量初始化和构造函数方法调用。 但是我的老师说我几乎是对的。后面的两件事是正确的,除了内存堆。相反,他说发生了内存分配。我认为对象存储在堆中,所以我的老师错了。你这样认为吗? 问题答案: 与往常一样,找到针对此类问题的解决方案的最佳位置是Java语言规范。 具体来说,从创建新实例的部分可以

  • 当我们将一个对象作为键插入到一个映射中时,它的哈希代码就会生成。但如果我的密钥是对象列表,那么,它是列表中所有对象哈希代码的总和吗? 请帮助理解。

  • 问题内容: 假设我有一个查询发送到我的SQL-Server数据库,它花费了30秒钟以上,并且我的程序引发了SQL Query Timeout异常。查询是否仍然在我的数据库中徘徊,或者在引发异常后立即终止? 问题答案: 客户端使用注意事件向服务器发出查询超时信号。注意事件只是SQL Server客户端可以发送给它的TDS数据包的一种不同类型。除了连接/断开连接,T-SQL批处理和RPC事件外,客户端

  • 问题内容: 这可能不是现实情况,只是好奇地知道会发生什么,下面是代码。 我正在创建一组class对象。根据Java中的哈希概念,当我第一次添加包含“ a”的对象时,它将创建带有哈希码97的存储桶并将该对象放入其中。同样,当它遇到带有“ a”的对象时,它将在类UsingSet中调用覆盖的哈希码方法,并将获得哈希码97,那么下一步是什么? 由于我尚未重写equals方法,因此默认实现将返回false。