为什么 Java 的 `equals()` 默认不进行深度比较

Why doesn't Java's `equals()` do deep comparison by default

众所周知,一个对象的equals()方法,如果不被覆盖,就是一个"shallow comparison",相当于使用“==”运算符。 (例如,参见 https://docs.oracle.com/javase/tutorial/java/IandI/objectclass.html。)

问题:为什么Java默认不提供"deep-comparison"equals()方法?也就是说,将递归地对其每个实例变量调用 equals() 。最终,递归将到达原始类型并停止。如果此深度比较等于默认值,是否有任何缺点?

深度比较比比较两个引用要复杂和耗时得多。这对于简单的对象可能没问题,但是当你有非常复杂的数据结构时(例如,一棵包含一万个元素的树)系统应该如何知道 "deep" 应该如何比较?

对于大多数对象来说,引用相等是正确的实现。 "Deep" equals是为了维护状态的少数。你的提议 运行 不仅会解决这里描述的许多问题,而且对大多数类型来说都是错误的。

Are there any downsides if this deep-comparison equals was the default?

是的。其中包括:

  • 默认实现无法区分作为对象逻辑值一部分的引用和仅与其他对象关联的引用。例如,假设您有一个引用公司的人 class。您有两个具有相同名称、SSN、DOB 等的 Person 实例。其中一个引用了一家旧公司。您可能希望引用同一个人的两个 Person 实例相等,即使其中一个具有过时的关联。
  • 深度相等性测试通常会比当前默认值慢,甚至可能慢得多。通常这是不必要的。当前默认值确保等式测试总是很快,除非有人明确指定。
  • 深度比较需要处理引用中的循环。这将需要某种方式来记住已经遍历了哪些对象。这将需要内存来跟踪这些对象,可能需要大量内存。相等性测试可能会导致 OutOfMemoryError。

当前的默认实现速度很快,并且不做任何假设。这是一个很好的默认值。有时您需要覆盖默认值,使用您对对象逻辑值包含什么的了解,而不考虑其根物理表示。