如果我们不使用HashMap或HashSet,就不需要覆盖Java的hashCode()方法。

10
我的同事重写了equals()方法。我的回应是,你也重写了hashCode()方法吗?他的回答是因为我们不会使用哈希映射或哈希集合,所以重写hashCode()方法并不重要。这个回答正确吗?
6个回答

14
是的,他说得事实正确 - 但是,如果您有一天需要将对象放入基于哈希的集合中,您将不得不添加哈希码,这可能很烦人,而且在那一天,您可能会因为错过equals方法中的某些微妙之处而实现hashcode不正确...

考虑到大多数IDE都提供了自动生成equals/hashcode功能,我认为没有太多理由不同时创建两者。

另一种看待它的方式是:当您覆盖父类的方法时,应遵循该父类定义的契约。在Object的情况下,javadoc of equals非常清楚:

请注意,通常必须重写hashCode方法,无论何时重写此方法,以维护hashCode方法的一般契约,该契约规定相等的对象必须具有相等的哈希码。

因此,除非您真正有设计理由不重写hashcode,否则默认决策应遵循父类契约并同时覆盖或不覆盖两者


2
但另一方面,如果代码现在依赖于“equals”,那么“hashCode”很有可能落后于对“equals”的更改,再次导致其与之不一致。在我看来,最好的妥协是简单的return 1;——至少在技术上是正确的。 - Marko Topolnik
7
我对 return 1; 的问题在于,如果你之后使用了 HashSet,它很可能会导致难以调试的性能问题。相反,我会将 hashCode() 实现为 throw new UnsupportedOperationException(),这只需要最少的工作量,但是当你回来并且之后使用 HashSet 时,很明显你第一次没有费心实现 hashCode() - Louis Wasserman
1
@LouisWasserman 如果你真的不想实现哈希码,抛出异常是一个非常好的主意。 - assylias
@Marko,返回1可能是最糟糕的想法-如果会正常工作,除了将哈希映射转换为链表(目前,在Java中没有标准的开放地址图)。 - bestsss
@Marko,我的意思是我不使用IDE自动生成的hashCodeequals。IDE根本不知道如何高效地生成它们。我的观点是:如果想将一个类用作键,它必须像这样设计,包括不可变性。可变性不会影响将对象放入列表中,但会完全破坏它被包含在映射(集合)中。我宁愿有错误的hashCode,这样可以立即捕获它,而不是一个技术上正确的hashCode,一旦进入生产就有可能破坏性能。 - bestsss
显示剩余5条评论

5
如果你覆盖了equals()方法,也要覆盖hashCode()方法。即使现在不使用hashCode()方法,您或其他人可能会使用它。
要了解更多有关此主题的信息,请查看出色的SO答案

4

这是一个代码的坏味道。例如,Findbugs将在您覆盖hashCode或equals而未覆盖另一个时发出警告。您应该同时覆盖它们,以使它们相互一致(即,a.equals(b) => a.hashCode() == b.hashCode())。

现在稍加努力可能会在以后节省很多麻烦。


如果在一个不适合作为键的类上(例如可变类)使用hashCode,可能会在以后给你带来很多麻烦。如果没有重写hashCode,这将立即被捕获,而破坏了hashCode则更难追踪。 - bestsss
如果它不应该是哈希结构中的键,您可以在哈希码内部抛出异常...这方面已经有先例,例如在Google协议缓冲区中,当协议消息包含MessageSet时。 - sjr
虽然这是一个不错的解决方案,但有可能会破坏现有的代码(总的来说,没有人希望hashCode()抛出异常)。我知道有些代码使用hashCode()测试是否为空(即期望NPE)[虚假的getClass()可能会更好] - 所以再次提议将其设置为默认选择有点冒险。 - bestsss

1

每个覆盖equals方法的类都必须重写hashCode方法。否则,将违反Object.hashCode的通用契约,这将导致您的类无法与所有基于哈希的集合(包括HashMap、HashSet和Hashtable)正常配合使用。

《Effective Java》第9条:覆盖equals时总要覆盖hashCode。

如果您的类是公共类,则无法控制未来开发中如何使用您的类。除非查看源代码(或通过反射),否则没有办法知道此类是否已重写hasCode方法,如果用户在任何基于哈希的集合中使用它,他们将会受到结果的惊吓。


没错!我认为我的同事可能没有考虑过发布他的代码供其他人使用,而且他可能会成为这个框架的主要开发者。 - user1739658

0

一般:

只覆盖那些你想以自己的方式使用的方法,以及那些受此覆盖影响的方法。

针对您的问题:

从Java文档中可以看到:

请注意,通常需要重写hashCode方法,每当equals()方法被重写时,以维护hashCode方法的一般契约,该契约规定相等的对象必须具有相等的哈希码。

Equals


这通常是正确的,但equals和hashcode确实意味着要保持一致:覆盖它们中的一个或两个都是默认选择,使它们不一致(只覆盖其中一个)是一个重要的设计决策。 - assylias
我认为现在我的回答有些道理了。 - Arpit

0

假设您创建了两个不覆盖equals和hashCode方法的不同对象。然后,如果您调用equals方法,Java会隐式地调用hashCode方法,然后检查哈希码的相等性。

覆盖hashCode方法就足以检查两个对象的相等性。这对未来很有用。您可以在集合中使用此类。

否则,如果您只实现了equal方法,它只能解决两个对象的相等性问题,而不能更多。


Java在默认实现equals时不会隐式调用hashCode。它使用==来验证两个对象是否指向同一引用。即使你让hashCode在每次调用时返回一个随机值,只要你指向同一个对象,equals的默认实现仍然可以正常工作。 - Marc Baumbach
你对使用hashCode()方法检查相等性的说法是错误的。拥有相同哈希码并不能保证它们相等。 - ATrubka
是的,Mark,你说得对,它检查引用。在我的回答中,我特别指出了Object类的默认实现将用于我的示例。其他可能性没有在我的回答中指定。 - emin

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