为什么我们有了“==”操作符还需要equals()方法?

35

当我查看equals()方法的实现时,它什么也不做,与==操作符所做的一样。因此我的问题是,既然有==操作符可以完成相同的工作,为什么还需要将其作为独立的方法存在呢?

8个回答

44

你不能重载== 运算符,但是如果你想让它的行为与== 运算符不同,即不仅比较引用还要实际比较对象(例如使用所有或一些字段),则可以重写equals(Object) 方法。

另外,如果你重写equals(Object) 方法,也要查看hashCode() 方法。这两个方法需要兼容(即根据equals(Object) 方法相等的两个对象需要具有相同的hashCode() 值),否则会发生各种奇怪的错误(例如当将对象添加到集合或映射中时)。


+1你的答案更可能是正确的。 - Petrus Theron
如果您使用相同的字段实例化两个不同的对象,并在这些对象上设置相同的字段,则测试它们是否相等将返回false。如果您重写类中的Equals()方法,那么您可以测试这些字段并在它们相同时返回true。 - David Neale
5
平等并不等同于完全相同。两张十元纸币(在货币的模型中)是平等的,但它们并非同一张纸币。 - ptomli
同意 - 这就是为什么如果必要的话,人们必须手动覆盖标准的相等行为来实现这一点。 - David Neale
我不明白你为什么在谈论覆盖。在这个问题的背景下,我觉得这是无关紧要的(抱歉)。 正如其他答案所说,==比较对象引用,equals()比较对象内容。 - Jean-Philippe Caruana

35

==比较对象的引用(reference),并询问这两个引用是否相同。

equals()比较对象的内容(contents),并询问这些对象是否代表相同的概念。


2
除非你在比较值类型... - David Neale
4
@David:在Java中,除非你在谈论原始值,否则不存在“值类型”。 - Joachim Sauer
抱歉 - 我的意思是原始类型,你提到Java不能创建值类型的观点很好。 - David Neale
但是原始类型没有equals()函数,只有它们的自动装箱类型才有。如果例如数字小于200或小于100,则结果也将相等,现在不知道。System.out.println(new Integer(55).equals(new Integer(55))); System.out.println(new Integer(5555).equals(new Integer(555)));打印true false - Daniel
@david:我认为不用说,原始类型没有方法,因此任何明智的人都应该做出这种区分。 - user132014
@Tom。说得好。当我阅读这个问题时,我起初没有注意到它的Java标签,所以我默认使用了C#。 - David Neale

19

对于基本类型==运算符检查两个值是否相同。如果不是基本类型,则检查它们是否是指向同一对象实例的两个指针(或引用)

equals()方法执行自定义检查,在Object中通过使用==检查引用。但是在其他类中,有时会重写 equals()方法,它必须检查内容

例如:

int i0 = 34;
int i1 = 34;
int i2 = 35;
// results
i0 == i1: true
i1 == i0: true
i2 == i0: false

但是如果我们有非基本类型

String str0 = new String("Hello man!");
String str1 = new String("Hello man!");
String str2 = new String("!nam olleH");
String str2copy = str2;
// Results
str0 == str1: false // Pointer to two different object, so == will give false
str1 == str2: false // Idem
str2 == str2copy: true // So this are two pointers to the same object
str0.equals(str1): true // This are not the same objects, but they are equal
str1 == str1: true // Again: two times a pointer to the same  object
所以,为什么str0.equals(str1)返回true? 因为String类覆盖了equals()方法。在这个方法中,它不通过执行return this == obj;来检查它们是否相等。但在这个方法中,有一个完整的检查过程。我不知道他们用哪种方法来比较两个字符串,但这里有两种可能的方式:
  • 从这两个字符串生成哈希码并检查它们是否相等(int == int
  • 逐个字符地检查它们是否相同。
所以我希望现在这一点已经清楚了。

这是一个不错的总结。另外需要注意的是,在使用字符串字面常量时,行为又会有所不同... String str0 = "Hello man!"; String str1 = "Hello man!"; str0 == str1; 会返回true,因为JVM将字面字符串对象放置在字符串池中。因此,str1和str2都引用池中的同一对象。 - mmccomb
这里有点挑剔,但是根据定义,两个值永远不会相同(否则,它只是一个值)。 - fredoverflow

2

这两者之间有非常重要的区别。

"==" 比较对象实例。默认的 equals() 方法也是这样做的。请运行并分析以下代码示例:

public class Person{
   String name;

   public Person(String name){
       this.name = name;
   }

//overriding equals
public boolean equals( Object obj ) {
    if( this == obj )
        return true;
    if( obj == null )
        return false;
    if( getClass() != obj.getClass() )
        return false;
    Person other = (Person) obj;
    if( name == null ) {
            if( other.name != null )
            return false;
    } else if( !name.equals( other.name ) )
        return false;
    return true;
    }
     }

    ...
    ...
    Person john1 = new Person("John");
    Person john2 = new Person("John");
    System.out.println("john1 == john2:" + (john1 == john2));
    System.out.println("john1.equals(john2):" + john1.equals(john2));

正如您所看到的,“==”会返回false(这两个Person对象是不同的实例),而equals将返回true(因为我们定义了当两个Person具有相同的名称时它们是相等的)。


2

"==" 运算符用于比较引用。 equals() 方法是在对象定义上定义的。

Dog d =new Dog();
Collar c =new Collar("Red");
 d.setCollar(c);
Dog d2=new Dog();
 Collar c2=new Collar("Red");
d2.setCollar(c2);

 d2.getCollar() ==d.getCollar()

返回 false,表示这两只狗有两个不同的项圈对象。它们不共享相同的项圈。

d2.getCollar().equals(d.getCollar())

如果颈圈定义为[颈圈的颜色相同,则颈圈相同],则返回true。 这两只狗有相同颜色的颈圈。

   class Collar{
    String color="";
    public Collar(String p0){
    this.color=p0;
    }
    boolean equals(Object c){
      Collar other=(Collar)c;
      return  this.color.equals(other.getColor());
    }

    public String getColor(){
      return this.color;
    }
    }

1

这样做是为了使这成为可能:

String s1 = new String("foo");
String s2 = new String("foo");

System.out.println(s1 == s2); // false?! Different references!
System.out.println(s1.equals(s2)); // true

如果你查看String#equals()的源代码,你会发现它已经适当地重写了Object#equals()方法,以比较彼此的内部字符数组(即实际值)。许多其他类也对这个方法进行了重写。


0
在Java中,等号运算符(==)用于操作两个变量的数据,如果操作数是原始数据类型,则可以使用。但是,如果操作数是对象,则Java使用引用进行比较,因为它无法确定要比较对象的哪个字段或字段。
因此,基于用户定义的字段只有一种比较方式,即通过覆盖equals()方法在对象中定义,因为等号运算符(==)不能在Java中被覆盖,因为Java不支持运算符重载。
例如,如果您想根据名称比较Employee,则需要通过在Employee类中覆盖equals方法来定义其逻辑,如下所示:
public class Employee {
    private Integer id;
    private String name;

    @Override
    public boolean equals(Object obj) {
        Employee other = (Employee) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }


}

-1

字符串 "string" == "string" 将返回 false。 "string".equals("string") 将返回 true。

使用 o1 == o2 比较对象 1 是否与 o2 相同(按引用比较)。

对于 o1.equals(o2),根据对象,equals 方法可能被重写并且不会实现类似于 "return o1 == o2" 的内容。

例如,您创建了两个 Set 实例。这两个 set 对象是两个不同的对象,您可以在任何一个对象中添加不同的元素。set1 == set2 总是返回 false,但是如果 set2 包含与 set1 完全相同的元素,则 set1.equals(set2) 最终将返回 true,并且因为 Set 类中已重写 equals 方法...

Set 的 Equals 实现为:

      public boolean equals(Object o) {
        if (o == this)
           return true;

        if (!(o instanceof Set))
             return false;
        Set s = (Set) o;
        if (s.size() != c.size())
             return false;
        return containsAll(s); // Invokes safe containsAll() above
    }

1
我猜你的意思是 string1 == string2string1.equals(string2) —— 你回答中的这两个例子都会返回 false。 - Samir Talwar
抱歉,我想表达的是 "string" == "string" 和 "string".equals("string")。 - Sebastien Lorber
6
"string" == "string" 这个表达式实际上会被解释为真。 - jarnbjo

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