"== 运算符和 equals() 方法有什么区别?(带 hashcode() ???)"

4

我正在深入学习哈希码,并发现以下内容:

1. 如果你重写了equals()方法,你必须同时重写hashcode()方法。

2. 要判断两个对象是否为同一对象,请使用==运算符。

考虑到这两个因素,在Java中,当使用==运算符比较两个实例是否相同时,

if(object1 == object2)

实际上正在进行的是

if(object1.hashcode() == object2.hashcode())

但是通过运行下面的测试,我发现我错了。

public class Main {

    public static void main(String[] args){
        Obj1 one = new Obj1();
        Obj1 two = new Obj1();
        //is this calling hashCode() in backend???
        if(one == two) {
            System.out.println("same");
        }
        else {
            System.out.println("nope");
        }
        //this is of course return true
        if(one == one) {
            System.out.println("one and one is same");
        }
    }
}

class Obj1 {
    @Override
    public int hashCode() {
        System.out.println("hashCode() is called");
        return 111;
    }
    @Override
    public boolean equals(Object another) {
        System.out.println("equals() is called");
        return false;
    }
}

根据使用 == 运算符 进行的测试,并且没有调用 equals() 方法。因此,我的问题是如果可以使用 == 运算符 来比较对象是否相同,那么为什么要重写 equals()hashCode() 方法进行比较?难道 == 运算符 已经完成了这个工作吗?
参考文献: Overriding hashCode() - is this good enough? http://mindprod.com/jgloss/hashcode.html http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Object.html#equals(java.lang.Object)

@所有人:感谢大家的解释。这就是我喜欢stackoverflow的原因。有很多聪明的人可以描述并给出好的例子,远比只提供定义的官方文档要好得多:D - Meow
6个回答

6

“==”运算符用于判断两个引用是否指向同一个对象。

因此,

 Object o1 = new Object();
 Object o2 = o1;

 o1 == o2; //true

 o2 = new Object();

 o1 == o2 // false

Object.equals()方法的作用是“如何确定两个指向不同对象的引用是否相等?”

如果两个引用指向同一个对象,则两者都

o1 == o2 
o1.equals(o2) 

应该是真的。

但是如果o1和o2不是同一个对象,它们在逻辑上仍然可能相等。对于任何给定的类,equals方法取决于对象背后的语义。例如,考虑一个类,其中field1和field2由用户设置,但field3是计算出来的,并且其计算具有随机性质。在这种情况下,定义equals方法只依赖于field1和field2而不是field3可能是有意义的。这就是为什么equals方法是必要的。


那么什么时候会使用hashcode()呢?我们是否可以决定在equals()方法中使用hashcode进行比较? - Meow
2
hashcode被许多基于哈希的集合(例如HashSets)使用。在equals中是否使用hashcode取决于您,但请注意,如果两个对象返回相同的hashcode并不总是意味着它们相等。我不会这样做。 - hvgotcodes

5

== 是身份标识。

.equals() 是相等性。

.equals() 默认只使用 ==(就像 hashCode() 默认为 System.identityHashCode() 一样),但如果有更有意义的方法来检查相等性,则可以覆盖它们。通常这是一种“结构”相等性。即:所有的部分 this 的内容是否与所有的部分 that 相等?


2

大部分问题已经得到解答,这里只是另一个启示性的例子:

String s1 = "foo";
String s2 = "foo";
System.out.println(s1 == s2); // true, because same reference (string pool)

String s3 = new String("foo");
String s4 = new String("foo");
System.out.println(s3 == s4); // false, because different reference
System.out.println(s3.equals(s4)); // true, because same value

2

==(用于对象而不是原始值)用于测试两个对象是否实际上是同一个对象;它比较指针是否实际上指向相同的内存位置。

.equals()由对象本身定义。

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

boolean b1 = ( s1 == s2 ) ; // false: s1 and s2 point to different objects
boolean b2 = ( s1.equals(s2) ) ; // true: s1 and s2 both represent the same
                                 //       piece of text - "Hello"

.hashCode()是一个优化技巧(在大多数情况下都是如此)。标准库中的很多代码都假设如果o1.equals(o2)==true,那么o1.hashCode()==o2.hashCode(),如果o1.hashCode()!=o2.hashCode(),则o1.equals(o2)==false,以便更快地工作。

这种优化最明显的例子是HashMap类。这使得使用键检索对象非常快,但如果hashCode和equals不能正确地为键元素工作,则会出现严重问题。实际上,这是String类是不可变的原因之一:如果您能够修改String(并因此更改其hashCode),而该String是HashMap中的键,则永远无法找到它,因为您最终会在错误的位置查找它!

其他答案推荐由Joshua Bloch编写的Effective Java。如果您正在提出这样的问题,那么现在正是购买这本书并从头到尾阅读的完美时机。在一两年后,再重新阅读一遍,当您忘记其中一些内容时,更多的内容将变得清晰明了...


2

如果您还没有一本《Effective Java》的副本,请购买一本 Joshua Bloch 的作品。

这是Java开发人员的事实标准参考书,其中包含了很多关于这个(以及许多其他)主题的信息。


这个周末要去书店 :D 那将是我的圣诞礼物! - Meow
@Fortyrunner,你能告诉我那本书的哪些章节解释了这些内容吗? - samsamara
书中的第8项和第9项。 - Fortyrunner

0

== 运算符 --> 检查两个引用是否指向同一个对象。如果相同,则返回 true,否则返回 false。

equals( ) --> 检查引用和状态对象。这里的状态指的是对象数据。如果其中任何一个为 true,则返回 true。否则返回 false。但我们必须在用户定义的对象中重写 equals( ) 并编写适当的代码。

Hashcode( ) --> 对象的哈希码只表示一个随机数,JVM 可以在将对象保存/添加到 Hashsets、Hashtables 或 Hashmap 中时使用它。

hashcode() 的示例:

class TestHasChode
{
int i;
TestHasChode(int i)
{
this.i = i;
}

public static void main(String arg[])
{  
    //before overriding hashcode()  
TestHasChode t1 = new TestHasChode(100);   
TestHasChode t2 = new TestHasChode(110);

System.out.println(t1); //TestHasChode@45e41830  
System.out.println(t2); //TestHasChode@1f01b29  

TestHasChode t3 = new TestHasChode(100);  
TestHasChode t4 = new TestHasChode(100);  
System.out.println(t3); //TestHasChode@3a8721bd   
System.out.println(t4); //TestHasChode@7db81d4f

/*hashCode() of Object class implemented to return hashCode based on address of an object, but based
on our requirement we can override hashCode() to generate our own numbers as hashCodes*/

//after overriding hashcode()  
System.out.println(t3); //TestHasChode@64  
System.out.println(t4); //TestHasChode@64  
}  
public int hashCode(){
return i;
}  
}  
-->Example of equals()method      
class Student
{
String name;   
int rollno;   
Student(String name,int rollno)
{   
this.name = name;    
this.rollno = rollno;   
}   
public static void main(String arg[])
{       
    //before overrideng equals method    
Student s1 = new Student ("raju", 101);     
Student s2 = new Student ("giri", 102);     
Student s3 = new Student ("giri", 102);     
System.out.println(s1.equals(s2));//false    
System.out.println(s2.equals(s3));//false    
    //after overriding equals method    
System.out.println(s1.equals(s2));//false    
System.out.println(s2.equals(s3));//true-->hear overriding equals() checks state.so it is true.  
    //in string variables comparisition   
    String s4="hello";    
    String s5=new String("hello");    
    String s6=new String("hello");   
System.out.println(s4.equals(s5));//true--> because String class containg overridden equals method   
System.out.println(s5.equals(s6));//true-->even though differnet object reference but String class containg overridden equals method   

}     
public boolean equals(Object obj){   
String name1 = this.name;   
int rollno1 = this.rollno;   
Student s2 = (Student)obj;     
String name2 = s2.name;     
int rollno2 = s2.rollno;    
if(name1.equals(name2) && rollno1 == rollno2){   
return true;}   
else{     
return false;}  
}        
}

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