4

没有人真正谈论equals()hasCode()的这一方面,但对equals()hashCode()行为可能产生巨大影响。在处理引用其他对象的更复杂的对象时非常重要。

Joshua Bloch 在他的 Effective Java 中甚至没有在他的“覆盖 equals() 方法”一章中提到它。他所有的例子都是像 Point 和 ColorPoint 这样的琐事,都只有原始或接近原始的类型。

可以避免递归吗?有时几乎没有。认为:

Person {
    String name;
    Address address;
}

这两个字段都必须转到业务键(正如 Hibernate 的人所说的那样),它们都是值组件(正如 Joshua Bloch 所拥有的那样)。Address 本身就是一个复杂的对象。递归。

请注意,Eclipse 和 IntelliJ 等 IDE 确实会生成递归 equals() 和 hashCode()。默认情况下,它们使用所有字段。如果你大量使用生成器工具,你就会自找麻烦。

一个麻烦是你会得到一个StackOverflowError。这是我的简单测试证明它
所需要的只是将另一个对象作为“值组件”的类,形成一个对象图和推荐的 equals() 实现。是的,您需要在那个循环中绘制图表,但这并非不切实际(想象分子、地图上的路径、相互关联的交易......)。

另一个麻烦是性能。equals() 推荐的实际上是比较两个对象图,可能是巨大的图,最终可能会在不知情的情况下比较数千个节点。并不是所有的都在内存中是必需的!考虑到某些对象可能是可延迟加载的。一个 equals() 或 hashCode() 调用最终可能会加载一半的数据库。

悖论是,你越严格地重写 equals() 和 hashCode() 就像你被鼓励做的那样,你就越有可能遇到麻烦。

4

2 回答 2

0

理想情况下,equals() 方法应该测试逻辑相等。在某些情况下,它可能比物理对象下降得更深,而在其他情况下,它可能不会。

如果测试逻辑相等不可行,由于性能或其他问题,那么您可以保留 Object 提供的默认实现,而不依赖 equals()。例如,您不必将对象图用作集合中的键。

布洛赫确实这样说:

避免问题的最简单方法是不要重写 equals 方法,在这种情况下,类的每个实例都只与自身相等。

于 2013-04-17T15:54:19.533 回答
0

至少有两个逻辑问题,这对于任何类型的任何两个引用都是有意义的,并且在不同时间equals对测试很有用:

  1. 该类型能否保证这两个引用将永远标识等价对象?

  2. 只要保存引用的代码既不修改对象,也不将它们暴露给可以这样做的代码,该类型是否可以保证这两个引用将标识等效对象?

如果一个引用标识了一个可能随时更改而不另行通知的对象,则唯一应该被视为等效的引用是那些标识相同对象的引用。如果一个引用标识了一个深度不可变类型的对象,并且从不用于测试其身份的方式(例如锁定IdentityHashSet,等等),那么所有对拥有相同内容的对象的引用都应该被认为是等效的。在上述两种情况下,正确的行为equals是明确和明确的,因为在前一种情况下,两个问题的正确答案将通过测试参考身份获得,而在后一种情况下,正确答案将通过测试深度相等性获得.

不幸的是,有一个非常常见的场景,这两个问题的答案存在分歧:当对可变类型对象的唯一现存引用由代码持有时,该代码知道对这些对象的引用永远不会被可能改变它们或测试的代码持有他们作为参考身份。在那种情况下,如果两个这样的对象目前封装了相同的状态,它们将永远这样做,因此等价应该基于成分的等价而不是引用身份。换句话说,equals应该基于嵌套对象如何回答第二个问题。

因为相等的含义取决于仅由引用的持有者知道的信息,而不是由引用标识的对象,所以equals方法实际上不可能知道哪种风格的相等是合适的。知道它们所引用的事物可能会自发改变的类型应该测试这些组成部分的引用相等性,而知道它们不会改变的类型通常应该测试深度相等性。集合之类的东西应该允许所有者指定存储在集合中的东西是否可以自发改变,并在此基础上测试相等性;不幸的是,相对较少的内置集合包含任何此类工具(代码可以在例如HashTableIdentityHashTable区分什么样的测试适合keys,但大多数集合没有等价的选择)。最好的办法可能是让每个新的集合类型在其构造函数中提供封装模式的选择:将集合本身​​视为可能在没有通知的情况下更改的东西(报告集合上的引用相等),假设集合将保持不变对可能在没有通知的情况下更改的事物的引用集(测试内容上的引用相等性),假设集合和组成对象都不会改变(测试equals每个组成对象),或者 - 对于数组集合或嵌套集合不支持深度相等测试——执行超深度相等测试到指定深度。

于 2014-07-16T17:14:49.650 回答