equals, ==, compareTo的区别
问题描述:
bobo老师,有个问题困扰我很久了。您也提到过但我一直不太明白。判断node == null是正确的,但我用node.equals(null)就会报错,这是为什么呢?还有a.equals(b) 和 a==b 以及a.compareTo(b)==0 是一样的吗?谢谢bobo老师!!
正在回答
== 比较的是两个类对象指向的是否是同一个内存地址。
比如:
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积分~
来为老师/同学的回答评分吧
0 星