在Java中,一个对象的hashCode是否应该更改?

5
似乎许多类(例如HashSet)都假定对象的hashCode不会改变。 在文档中很清楚地说明了equalshashCode之间的关系。
但是,实现一个在对象生命周期内会更改的hashCode是否是不良设计?

1
我认为你问题背后的真正问题只是这样一个简单的问题:什么时候应该使用不可变对象,而我所理解的答案是:尽可能地使用。可变对象总是必要的,但我的理解是,当可能时应该尽量限制它们,因为可变性会增加复杂性。 - Hovercraft Full Of Eels
5个回答

6

在应用程序中,至少需要有一个点,在这个点上,hashCode在关心它的集合中被冻结。通常情况下,在构建对象时(例如,添加到ArrayList中),hashCode会发生变化,然后您将其添加到集合中并停止更改。稍后,如果您从集合中删除它,则可能会再次突变。我认为,使用不可变数据结构(如String或自己定义的类型,其中包含所有final)与依赖于hashCode的集合(例如HashMap键或HashSet)通常是最佳实践。


1
顺便说一下,类似的事情也会发生在像HashSet、HashMap、PriorityQueue等类似对象中——如果对象的值将要改变,那么你应该先将其删除,然后更改其状态,再重新放回去。否则,它将会处于结构中错误的位置,导致程序出错。 - yshavit
抱歉,我的意思是TreeSet,TreeMap等等 :) - yshavit

3

当可变对象改变其内部状态时,hashCode发生变化是可以的。

但是,一旦该对象处于期望常量hashCode的位置,应用程序必须确保该对象不会被修改以使hashCode发生变化。


这不是问题所在。例如,一个不可变的相同的“String”可能在一个JVM运行到另一个JVM运行时具有不同的哈希码。 - fge
这里的“life-time”是指相同的生命周期吗?除非我们在思考某种转世哲学。 - ZhongYu

1
这取决于你所谓的“生命周期”。你提供的链接明确表示:
“在 Java 应用程序执行期间,如果对同一对象调用 hashCode 方法超过一次,则必须始终返回相同的整数。”
这意味着不能保证对象的哈希码在两个不同的应用程序运行中保持一致。
但是,在给定的应用程序运行中(即运行 Java 代码的 JVM 实例),对象的哈希码将永远不会改变。
该协议确保了这一点,但没有更多保证。

我没有点踩,但是引用继续说:“只要在对象上使用的等式比较中没有修改任何信息”。因此,在单个运行期间hashCode似乎并不保证/需要不会改变。 - Cinnam
@Cinnam 但那是必然的!这不是原发布者的问题所在...或者如果是,我已经完全误解了问题。 - fge
我理解 OP 的问题是“如果遵守 hashCode()/equals() 关系,一个对象的 hashCode() 方法是否可以返回不同的值?”。我的评论只是关于你回答中提到的一部分,即在单个执行期间哈希码保证不会改变。我认为你的意思是即使你相应地更改了 equals(),也不能更改它。 - Cinnam

0
你在谈论不同的事情。 如果你想使用 Hash(Map|Set) - 键应该是不可变对象。 因此,在这种情况下,hashCode 也将是不可变的。 但在一般情况下,hashCode 应该随着对象状态改变(根据对于 hashCode 有意义的字段)。

0

hashCode不能保证在不同的执行中返回相同的结果。正如Javadoc所指出的:

在Java应用程序的执行过程中,如果对同一对象调用多次hashCode方法,则只要equals比较中使用的信息没有被修改,hashCode方法就必须始终返回相同的整数。这个整数不需要在同一应用程序的不同执行之间保持一致。

虽然大多数hashCode实现提供稳定的值,但这种情况并不常见,甚至类库中的某些类还会指定它们用于计算哈希码的确切公式,例如String类。对于这些类,哈希码将始终相同。但是,您不能依赖于它。

此外,有些人认为hashcode是对象的唯一句柄。这是错误的和反模式的。例如,字符串"Aa"和"BB"产生相同的hashCode:2112。


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