是equals()应该是递归/深?

时间:2013-04-17 15:53:22

标签: java equals

没有人真正谈论 equals() hasCode()的这一方面,但是对 equals()有潜在的巨大影响 hashCode()行为。当处理引用其他对象的更复杂的对象时,它是大量的。

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

可以避免递归吗?有时很难。假设:

Person {
    String name;
    Address address;
}

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

请注意,Eclipse和IntelliJ等IDE会生成递归equals()和hashCode()。 它们默认使用所有字段。如果你将生成器工具应用于质量,你就会遇到麻烦。

一个问题是你可以得到 StackOverflowError 。我的simple test proving it 所有需要的是具有作为“值组件”另一个对象的类,形成对象图并推荐equals()实现。是的,你需要在那个周期中使用图表,但这并不是不现实的(想象分子,地图上的路径,相互关联的交易......)。

另一个问题是性能。 equals()的推荐实际上是比较两个对象图,可能是巨大的图,最终可能会在不知情的情况下比较数千个节点。并非所有这些都在记忆中是必要的!考虑一些对象可能是懒惰可加载的。最终可以在一个equals()或hashCode()调用上加载一半数据库。

悖论是,你越是严格地重写equals()和hashCode(),你就越有可能遇到麻烦。

2 个答案:

答案 0 :(得分:0)

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

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

布洛赫确实这么说:

  

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

答案 1 :(得分:0)

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

  1. 类型是否可以承诺两个引用将永远识别出等效对象?

  2. 只要保存引用的代码既不会修改对象,也不会将它们公开给代码,这个类型可以承诺两个引用将识别等效对象吗?

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

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

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