equals, ==, compareTo的区别

equals, ==, compareTo的区别

问题描述:

bobo老师,有个问题困扰我很久了。您也提到过但我一直不太明白。判断node == null是正确的,但我用node.equals(null)就会报错,这是为什么呢?还有a.equals(b) 和 a==b 以及a.compareTo(b)==0 是一样的吗?谢谢bobo老师!!

正在回答

登陆购买课程后可参与讨论,去登陆

1回答

== 比较的是两个类对象指向的是否是同一个内存地址。


比如:

public class Student {
    private String name;
    
    public Student(String name){
        this.name = name;
    }
    
    public static void main(String[] args) {
        
        Student a = new Student("liuyubobobo");
        Student b = new Student("liuyubobobo");
        
        System.out.println(a == b); // 打印 false
    }
}


上面的代码打印出来是 false,虽然 a 和 b 两个 student 都叫 liuyubobobo,但是因为两个类对象是分别 new 的,所以他们的内存地址不一样。


对于自定义类,默认直接打印这个类对象,就能得到这个内存地址:

public class Student {
    private String name;
    
    public Student(String name){
        this.name = name;
    }
    
    public static void main(String[] args) {
        Student a = new Student("liuyubobobo");
        Student b = new Student("liuyubobobo");
        
        System.out.println(a == b); // 打印 false
        
        System.out.println(a);
        System.out.println(b);
    }
}


你可以在你的环境下打印一下这个结果,应该是类似这样的输出,其中 @ 后面就是这个类对象对应的内存地址:

Student@61bbe9ba
Student@610455d6


但如果是这样,b = a 以后,b 和 a 就指向同一个内存对象,a == b 就是 true,并且 a 和 b 打印出来,内存地址是一样的:

public class Student {
    private String name;
    
    public Student(String name){
        this.name = name;
    }
    
    public static void main(String[] args) {
        Student a = new Student("liuyubobobo");
        Student b = a;
        
        System.out.println(a == b); // 打印 true
        
        System.out.println(a);
        System.out.println(b);
    }
}


a == null,判断的就是 a 的内存地址是否是 0。因为 0 这个内存地址不能存储任何程序中的变量,所以,如果 a 中存储的内存地址是 0,我们就说 a 其实什么都没有,就是空的。


===============


但是,很多时候,我们不希望做内存地址的比较。比如:

Student a = new Student("liuyubobobo");
Student b = new Student("liuyubobobo");

a 和 b 都是 liuyubobobo,我们希望判他们等,怎么办?这就叫做我们要判“值”是否相等(而不是地址是否相等。)答案是,我们要覆盖 equals 方法:


简单的写一个 equals 方法是这样的:

public class Student {
    private String name;
    
    public Student(String name){
        this.name = name;
    }
    
    @Override
    public boolean equals(Object student){
        return this.name.equals(((Student)student).name);
    }
    
    public static void main(String[] args) {
        Student a = new Student("liuyubobobo");
        Student b = new Student("liuyubobobo");
        
        System.out.println(a == b); // 打印 false
        System.out.println(a.equals(b)); // 打印 true
    }
}


在这段程序中,a == b 打印出来是 false,因为他们的内存地址不一样;a.equals(b) 打印出来是 true,因为根据我们定义的 equals 方法,他们的值一样。


注意,我们现在定义的这个 equals 方法是有 bug 的,这个 bug 是什么?就是如果我们传入的是 null,那么根据逻辑,我们就会把 null 强转成 Student,然后尝试调用 null 的 name 属性,这显然是不合法的,所以就会报错。


这就是你的问题中的报错,这个报错不是因为不能在 equals() 中传 null,而是因为你的 equals 逻辑中对于 null 会发生错误。


这也是为什么,在这个课程开始,我带领大家写的 equlas 方法是这样的。这是 Java 的标准的 equals 的写法,防止了类似这样的问题:https://git.imooc.com/class-105/Play-Algorithms-and-Data-Structures/src/master/02-Linear-Search/05-Using-Custom-Class/src/Student.java


请仔细理解一下,这段代码每一步都在做什么?为什么可以防止 null 的问题:

public class Student {
    
    private String name;
    
    public Student(String name){
        this.name = name;
    }
    
    @Override
    public boolean equals(Object student){
        if(this == student)
            return true;
        
        if(student == null)
            return false;
        
        if(this.getClass() != student.getClass())
            return false;
        
        Student another = (Student)student;
        return this.name.equals(another.name);
    }
    
    public static void main(String[] args) {
        Student a = new Student("liuyubobobo");
        System.out.println(a.equals(null)); // equals 传入 null,不会报错
    }
}

这段程序的打印结果是 false。我们在 equals 里传入了 null,但是不会报错。


==========


最会,compareTo 非常简单了,equals 只返回 boolean 值,表示等于或者不等于;compareTo 返回一个整数值,可以是负数,零或者正数,表示小于,等于,和大于。


所以,我们在 前面学习的 LinearSearch 中,只使用 equals 就好了,因为对于查询,我们只关注是否相等;但是在排序算法中,我们需要使用 compareTo,因为对于排序,我们不仅要看两个元素是否相等,还需要看他们之间的大小关系(如果不等的话,谁更大,谁更小)。


==========


总结:


== 判断的是地址;

equals 判断的是值;

compareTo 不仅在比较是否相等,还在比较大小关系;


**********


注意:对于 == 来说,对于基本数据类型,是判断值的。


int a = 5, b = 5。 a == b 返回是 true 的。此时不是因为 a 和 b 的内存地址不一样,而是因为 a 和 b 的值一样。


Java 只有 8 种基本数据类型,可以搜索一下。


另外,因为你对这个问题有疑问,我强烈建议你系统学习一下 Java 语法,尤其是工作的主要语言是 Java 的话。《Java 核心编程》就很好。


但其实,如果你理解计算机的内存模型,这里其实是统一的。因为对于基本数据类型,内存中直接存值,而对于类对象,内存中存的是这个类的实际数据对应的地址。因为他们存的内容不一样,所以 == 的结果看似发生了变化,但它做的事情是一样的。


说得太复杂,可能你也一时不能消化,可以先用自己能够理解的方式理解一下,不用去想什么内存模型,学习计算机专业,以后慢慢一定对接触这些的。先搞明白这些符号在什么情况下行为是怎样的。像我一样,在需要的时候,用具体的代码去验证。


你问的这个问题非常基础,也非常重要,请一定要搞明白这个问题。每个语言中都涉及这个问题,只不过具体的表现形式和语法细节不一样,但他们的本质是相同的。


继续加油!:)


  • 蛋包饭吃球球 提问者 #1

    感谢bobo老师的详细回答!!我会慢慢理解吸收并认真对待您讲的知识和建议的!!

    2021-08-06 23:03:19
问题已解决,确定采纳
还有疑问,暂不采纳

恭喜解决一个难题,获得1积分~

来为老师/同学的回答评分吧

0 星
算法与数据结构
  • 参与学习       2583    人
  • 解答问题       1082    个

慕课网算法名师Liuyubobobo,5年集大成之作 从0到工作5年,算法与数据结构系统解决方案

了解课程
请稍等 ...
意见反馈 帮助中心 APP下载
官方微信

在线咨询

领取优惠

免费试听

领取大纲

扫描二维码,添加
你的专属老师