为什么要重写equals方法而不是使用另一个方法名

28

这似乎是一个愚蠢的问题,但为什么我们要覆盖 equals 方法而不是创建一个新的方法并使用新的名称进行比较呢?

如果我没有覆盖equals,那么 == 和 equals 都会检查两个引用是否指向同一内存位置吗?


6
那么对于使用可用的equals()方法的数据结构呢?例如Map、Set等? - TheLostMind
13
你也可以使用不同的名称来实现 toString(),但是除了你自己的代码外,其他代码都不会使用它。 - Thilo
非常感谢大家 :) - Lakshitha Ranasinghe
7
顺便提一下,我不认为这是一个愚蠢的问题。我认为理解Java对象模型非常重要。 - Michael Lorton
7个回答

57
这似乎是一个愚蠢的问题,但为什么我们要覆盖equals方法而不是创建一个新的命名方法并使用它进行比较?
因为所有标准集合(ArrayList, LinkedList, HashSet, HashMap,等等)在决定两个对象是否相等时都使用equals
如果您发明了一个新的方法,这些集合将不知道它并且无法按预期工作。
以下是非常重要的理解内容:如果诸如ArrayList之类的集合调用Object.equals,则此调用将在运行时解析为被覆盖的方法。因此,即使您发明了集合不知道的类,它们仍然可以在这些类上调用方法,例如equals
如果我没有覆盖equals,那么等于运算符(==)和equals方法都会检查两个引用是否指向同一内存位置吗?
是的。 Object.equals的实现只执行==检查。

最后一件事。如果我像这样创建了两个字符串 //String a = "Hello"; String b = "Hello"; 那么a和b的引用都指向同一个对象?这意味着我可以随时使用equals()方法来比较它们而不需要重写它吗? - Lakshitha Ranasinghe
在Java中,字符串是特殊的。如果你做 String a = "Hello"; String b = "Hello"; 那么 == 会起作用。 - aioobe
明白了,这就是为什么不能使用“==”来比较两个字符串内容的原因 :) - Lakshitha Ranasinghe
6
没错。通常情况下,在比较字符串时不能使用“==”符号 :-) (如果是编译时常量可能会起作用)。通常来说,当解释或思考equals和“==”的区别时,使用字符串作为例子并不是一个好选择。 - aioobe

9

如果你使用依赖于equals的类,比如HashMapHashSetArrayList等,那么你需要重写equals方法。

比如,如果你将自己的类的元素存储在HashSet中,如果你希望元素的唯一性不是通过简单的引用相等来确定,那么你必须重写hashCodeequals方法。

是的,如果你不重写equals方法,默认的实现(在Object类中实现)与==相同。


5
除了其他答案中提到的主要原因外,还需要考虑程序的可读性。
如果您重写equalshashCode方法,任何阅读您代码的人都知道这些方法的作用。这样做告诉读者类的实例之间价值相等的标准。使用equals方法阅读您代码的人将立即知道您正在检查值的相等性。
如果您使用其他名称,它只会混淆读者,并使他们花费额外的时间阅读JavaDocs以了解您的方法的作用。

4
由于equals()Object类的一个方法,它是所有类的超类,因此在您编写的每个类中默认存在。equals()用于对象比较,所以每个集合类或其他标准类都使用它。如果您希望其他类支持对自定义类对象进行相等性比较,则只需重写equals()(因为其他类知道调用equals()进行对象比较)。如果您仅使用自己的类,则可以创建一个新方法,并确保所有内容都使用您的自定义方法进行比较。

2
equals方法和hashcode方法是特殊的方法,在Java的实用类中广泛使用,特别是在集合框架和包装类(例如String、Integer)中已经被覆盖。因此,如果您将任何具有正确的equals和hashcode实现的对象放置在HashSet中,则为了维护唯一性属性,hashcode将与HashSet中所有现有对象进行比较。如果找到任何匹配的hashcode,则会查看equals方法以进一步检查它们是否真正相等,如果相等检查也通过,则拒绝传入对象。但是,如果hashcode相等性检查未通过,则HashSet不会使用equals方法,而是直接将该对象放置在HashSet中。因此,我们需要确保equals和hashcode的实现在逻辑上是正确的。

2
HashMap<T,U> 这样的类需要有一些手段来确定集合中的哪个项目(如果有)应被视为与给定项目等价。这可以通过以下两种方式之一实现:
  1. 要求要存储在集合中的任何东西都必须包含用于执行此类比较的虚拟方法(最好提供快速手段(例如 hashCode())来分配部分等价类)。
  2. 要求创建集合的代码必须提供一个对象,该对象可以接受对其他对象的引用并执行与等价关系相关的操作。
本来可以从 Object 中省略 equalshashCode(),并使像 HashMap 这样的类型仅能与实现了包含这些成员的 equatable 接口的键类型一起使用;希望使用由身份标识为键值的引用集合的代码将不得不使用 IdentityHashMap。这样的设计也不算不合理,但当前的设计使得一个使用 HashMap 的通用集合-of-集合类型可以与按值以及按标识进行比较的东西一起使用,而不必为 collection-of-HashMap 和 collection-of-IdentityHashMap 定义单独的类型。
一种替代设计可能是有一个 GeneralHashMap 类型,其构造函数需要指定一个比较函数,并且 IdentityHashMapHashMap 都从它派生而来;后者会将其类型限制为 equatable,并将其标识函数链接至其中包含的对象的标识函数。这种设计可能没有什么特别错误的地方,但事情并不是这样做的。
无论如何,集合都需要一些标准手段来确定应视为等价的项目;使用虚拟的 equals(Object)getHashCode() 就是一种方法。

1

问题1

有两件事情。 equals()位于Object类中, 集合框架在比较对象时使用equals()hashcode()方法。

问题2

是的,用于比较两个对象。但是当您比较两个String对象时,equals()仅检查值。


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