准备工作
在 Java 中,比较两个对象或者数据大小的时候,我们会经常用到 equals()方法、"=="和 compareTo() 方法。对于这三种方法,它们到底是如何进行比较的,以下做一个简单分析。
为了防止类中对 equals() 方法重写的干扰,在这里,我们自己创建一个类,自己决定是否对 equals() 方法进行重写,并由此更好的区分 " == " 与 equals() 方法的区别。
实现自定义类
这里实现一个 student 类,类包含 name,age,score 这三个属性. 并实现 Comparable 接口(compareTo 就是该接口中的方法)
class Student implements Comparable<Student>{
String name;
int age;
int score;
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
实现测试方法
此方法用于对以上三种方法进行测试.
方法实现的同时,先生成三个 student 对象,用于后面测试.
public class Test {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("wang",18,70);
students[1] = new Student("wang",18,70);
students[2] = new Student("hu",30,47);
}
“==”
请看代码:
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("wang",18,70);
students[1] = new Student("wang",18,70);
students[2] = new Student("hu",30,47);
System.out.println(students[0] == students[1]);
}
此时比较的是 两个地址不同值相同的
student 对象,结果如下:
那么,问题来了,值相同为什么结果还是 false 呢?
请看如下代码:
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("wang",18,70);
students[1] = new Student("wang",18,70);
students[2] = new Student("hu",30,47);
String a = "abc"; //
String b = "abc"; // 若不申请新的地址,对于相同字符串,它们的地址是一样的
String c = new String("abc"); // 申请新的地址
int a1 = 123;
int b1 = 123;
System.out.println(students[0] == students[1]);
System.out.println(a == b); // a 和 b 同时指向存储 'abc' 这个字符串的地址
System.out.println(a == c); // c 申请新的地址
System.out.println(a1 == b1); // 基本数据类型
}
输出结果:
上述我们看到了, 当数据类型为整形,当变量的值相等的时候,结果就为 true ,否则为 false.
当为 student 类对象 或者 String 类型,当它们的地址一样的时候,返回值才为 true,否则为 false.
结论:
- 对于
基本数据类型
(如 int、float等)," == " 比较的是值是否相等
,即内容是否一样; - 对于
引用数据类型
(如类、数组等),"==" 比较的是引用是否相等
;
备注:此处对于 float、double、数组等类型就不一一列举了,感兴趣的可以测试下,结果都是相似的
Java基本数据类型和引用数据类如下:
equals()
重写 equals() 方法
在测试之前,我们先自己在 student 类中实现 equals() 方法,如下:
class Student implements Comparable<Student>{
String name;
int age;
int score;
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
/*
* equals方法
* 在此,实现的是名字,年龄和分数都相同的情况返回ture
* 还会生成一个hashcode方法,这俩总是成对存在
* */
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && score == student.score && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age, score);
}
}
进行测试:
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("wang",18,70);
students[1] = new Student("wang",18,70);
students[2] = new Student("hu",30,47);
System.out.println(students[0].equals(students[1])); // 值一样
System.out.println(students[0].equals(students[2])); // 值不一样
}
结果:
不重写 equals() 方法
只需要将上面重写的方法注释掉即可:
class Student implements Comparable<Student>{
String name;
int age;
int score;
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
/*
* equals方法 比较的是名字,年龄和分数都相同的情况返回ture
* 还会生成一个hashcode方法,这俩总是成对存在
* */
/*@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && score == student.score && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age, score);
}*/
}
测试代码和上述一样,结果如下:
小结
重写 equals() 方法的时候,此时该方法只是比较值是否相等,也就是内容是否相等。当我们不重写 equals() 方法时,即使值一样也会返回 false,这是为什么呢? 这还得看看 equals() 方法的源码(即具体实现),如下:
当我们点击进来的时候,其实发现此时进入了 Object 类,其中的 equals() 方法其实还是通过 “==” 实现的。这样一来上述疑问就解决了,student 对象是引用类型(没有自己实现方法重写),进行比较的时候会自动调用 Object 类中的 equals() 方法,此时比较的是地址是否相等,显然 student[0] 与 student[1] 的地址是不相等的,所以返回 false。
当我们再来查看 String 类中 equals() 方法:
此时,先是比较的是地址是否一样,一样返回 true,不一样则进行值的比较,即内容相等就返回 true,否则为 false. 这显然是对 equals() 方法进行了重写.
总结(以及 equals() 与 " == " 的区别):
- equals() 方法是判断两个对象是否相等,分为两种情况:
- 类没有重写 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于使用 “==” 比较对象,即 判断地址是否相等;
- 类重写了 equals() 方法。一般类都会重写这个方法(例如 String 类,Integer等),此时是判断对象的值是否相等(内容是否相等),相等为 true,相反为 false;
即 equals() 方法默认情况下是地址比较,只是很多类重写了 equals() 方法,将 equals() 变为值比较,而一般类都会重写该方法,所以一般情况下,equals() 都是用值进行比较。
compareTo()
观察 compareTo() 方法的源码:
可以看出这里其实是获取两个字符串/对象的长度,然后做减法(根据ASCll码),compareTo() 方法就会返回一个 int 类型的数。若两个字符串相同,就会返回0,若前面的大于后面的,返回相差ASCll码(正数),反之,返回相差负数ASCll码。
看如下例子:
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("wang",18,70);
students[1] = new Student("wang",18,70);
students[2] = new Student("hu",30,47);
int ret = students[0].compareTo(students[1]); //compareTO方法
int r = students[0].compareTo(students[2]);
System.out.println(ret); //输出大于0表示前面大于后面的
System.out.println(r);
}
结果如下:
在这里,我自己重写了一个简易 compareTo() 方法(代码在下面),用 age 来进行比较,能够较好的说明了 compareTo() 方法的原理。
@Override
public int compareTo(Student o) { //用年龄进行比较
/*if(this.age > o.age){
return 1;
}else if(this.age < o.age){
return -1;
}else
return 0; */ //第一种写法
return this.age - o.age; //第二中写法 前面对象是this 后面对象是o
}
compareTo() 与 equals() 的区别:
- 返回结果不同:compareTo() 就是返回两者的差值,返回值是一个 int 类型,而 equals() 只是返回 true 或者 false;
- 比较方式不同:compareTo() 是差值比较,equals() 一般来说都是根据值是否相等进行比较;