在您的示例中,字符串是不可变的,因此其哈希码无法更改。但是假设,如果某个对象的哈希码确实是一个哈希表中的键而发生了变化,那么就哈希表查找而言,它很
可能会消失
。我在一个相关问题的答案中对此做了更详细的介绍:。(最初的问题是关于a
HashSet,但a
HashSet实际上是一个
HashMap底线,因此答案也涵盖了这种情况。)
为了补充@EJP的答案,如果您将a中的对象突变HashSet以使其重复(在equals/hashpre合同意义上),那么在实践中将会发生的事情是哈希表数据结构将中断。
根据突变的确切细节以及哈希表的状态,一个或两个实例将对查找(例如contains和其他操作)不可见。它要么在错误的哈希链上,要么是因为另一个实例出现在哈希链之前。而且很难预测哪个实例将是可见的……以及它是否仍将保持可见。
如果您对集合进行迭代,则这两个实例仍将存在…违反Set合同。
当然,从应用程序的角度来看,这是非常糟糕的。
您可以通过以下任一方法避免此问题:
为您的设置元素使用不可变的类型,
在将对象放入集合中和/或从集合中拉出对象时进行复制,
编写代码,使其“知道”在持续时间内不更改对象…
从正确性和鲁棒性的角度来看,第一种选择显然是最好的。
顺便说一句,以一般方式“解决”这一问题确实很困难。Java中没有普遍的机制来知道……或被告知……某些元素已更改。您可以在逐个类的基础上实现这种机制,但必须对其进行显式编码(而且价格不菲)。即使您确实有这种机制,您会怎么做?显然,现在应该从集合中删除其中一个对象…但是,哪个对象呢?
它是安全地说,如果任何一个HashMap或映像树的钥匙,影响他们各自的方式突变
hashpre()/
equals(Object)或
compare(...)或
compareTo(...)合同,然后将数据结构“破发”。
这是否意味着一旦将数据放入哈希集中,就不应对其进行更改。
是。
还是需要重新修复?还是自动完成等?
它不会自动修复。该
HashMap不会注意到一个关键的哈希码已经改变。确实,
HashMap调整大小后,您甚至都不会重新计算哈希码。数据结构会 记住
原始哈希码值,以避免在哈希表调整大小时必须重新计算所有哈希码。
如果您知道某个键的哈希码将要更改,则需要在对该键进行突变之前将其从表中删除,然后再添加回去。(如果您在更改密钥后尝试
remove/
put,则很可能
remove会找不到该条目。)
到底是怎么回事?
发生的情况是您违反了
HashMapjavadocs中明确规定的合同。不要那样做!



