Java對象的比較——equals方法,Comparable接口,Comparator接口
- 1. equals方法
- 2. Comparable接口
- 3. Comparator接口
1. equals方法
在判斷兩個整數是否相同時,我們可以使用以下方式:
System.out.println(1 == 2);
System.out.println(1 == 1);
如果輸出true,則說明這兩個整數相同;如果輸出false,則說明這兩個整數不相同
那么,如果將==用于判斷兩個對象,又會是怎樣的情況呢?我們直接敲代碼來看看!
public class Dog {private String name;private int age;public Dog(String name, int age) {this.name = name;this.age = age;}
}
public class Test1 {public static void main(String[] args) {Dog dog1 = new Dog("zhangsan",1);Dog dog2 = new Dog("zhangsan",1);System.out.println(dog1 == dog2);}
}
運行結果:
當運行這段代碼時,我們發現輸出的是false。明明dog1和dog2的屬性都一模一樣,為什么輸出false呢?莫慌,且聽我慢慢道來!
畫圖來分析,dog1和dog2中存放的值并不相同,因此dog1 != dog2。而你之所以認為輸出的應該是true,是因為你認為,兩個對象的屬性完全一致,所以dog1 == dog2.其實,并不是這樣的,==判斷的并不是對象的屬性是否一致,而是判斷兩個引用指向的是否是同一個對象!
在Java中,一切皆對象。而dog1和dog2是對象的引用。==判斷的并不是對象的屬性是否一致,而是判斷兩個引用指向的是否是同一個對象!
因此,只有當兩個引用指向同一對象才返回true;而如果兩個引用指向不同的對象,即使兩個對象的屬性完全相同,返回依舊是false!
總結:
- 如果==左右兩邊是基本數據類型變量,比較的是變量中的值是否相同
- 如果==左右兩邊是引用數據類型變量,比較的是引用變量中的值是否相同,而引用變量中存放的是對象的地址,所以比較的就是引用變量中存放的地址是否相同(即判斷兩個引用指向的是否是同一個對象!)
理解了這一點,學習equals方法就簡單多了!我們先來看看equals方法的原型,equals方法在Object類中
有沒有發現,equals方法,返回正是剛剛所講的內容,說明equals方法默認就是判斷兩個引用指向的是否是同一個對象!
注意: equals方法的返回值是boolean類型!
再來嘗試運行這段代碼:
public class Test1 {public static void main(String[] args) {Dog dog1 = new Dog("zhangsan",1);Dog dog2 = new Dog("zhangsan",1);System.out.println(dog1.equals(dog2));}
}
運行結果依舊是false
那么,問題來了,如果我們想通過比較兩個對象的屬性是否相等,如果相等,從邏輯上就說明他們就是相等的,該怎么辦呢?
我們知道Object類是一切類的父類,而equals方法在Object類中,是不是就通過可以重寫equals方法,從而達到自定義比較方式的目的!
下面就演示重寫equals方法,規則:如果兩個對象的屬性完全一致,則返回true;否則放回false
public class Dog {private String name;private int age;public Dog(String name, int age) {this.name = name;this.age = age;}public boolean equals(Object obj) {Dog tmp = (Dog)obj;return this.name.equals(tmp.name) && this.age == tmp.age;}
}
public class Test1 {public static void main(String[] args) {Dog dog1 = new Dog("zhangsan",1);Dog dog2 = new Dog("zhangsan",1);System.out.println(dog1.equals(dog2));}
}
這時運行代碼,輸出的就是true!
這時,有人可能就有疑問了,為什么名字比較要用equals方法,其實這里的equals方法并非Dog類中的equals方法,而是String類中的equals方法,用來判斷兩個字符串是否相等
String類中的equals方法原型:
另外,編譯器可以幫我們自動生成equals方法,第一步按住alt+insert
一路點下去,就可以生成equals方法,除此之外,還生成了hashCode方法
2. Comparable接口
通過重寫equals方法,我們可以從邏輯上去判斷兩個對象是否相同。但是如果要去比較兩個對象的大小,又該怎么去比較呢?對象的屬性那么多,通過什么屬性去比較呢?這時就需要講到Comparable接口!
當我們在Student類后加上Comparable接口時,發現會報錯,這是為什么呢?我們按住CTRL鍵,再鼠標左擊Comparable,進入源碼
我們發現,Comparable接口中還有個compareTo抽象方法,因此當我們在Student類要實現這個方法,除此之外,Comparable接口后還有個,這是泛型,我們需要比較什么類型的對象,就在實現接口時把T改成什么
由編譯器自動生成實現compareTo方法的代碼后:
我們就需要書寫compareTo的方法體,那怎么書寫呢?你想根據哪個對象的屬性進行比較,就怎么書寫。
比如,我現在想根據對象的年齡進行比較
下面進行測試:
public class Student implements Comparable<Student>{private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic int compareTo(Student o) {return this.age-o.age;}
}
public class Test {public static void main(String[] args) {Student student1 = new Student("zhangsan", 18);Student student2 = new Student("lisi", 10);System.out.println(student1.compareTo(student2));}
}
運行結果:
返回的是一個正數,說明根據年齡比較,student1大于student2
再比如,我不想根據年齡比較了,我想根據姓名比較,這時就不能用簡單的this.name-o.name了,因為name是一個String類型,不能通過這樣的方式進行比較
方法里的compareTo指并不是在Student類中具體實現的這個compareTo,而是String類中的conpareTo,用于字符串的比較,它的底層是和C語言的strcmp是一樣的,返回的是兩個字母的ASCII 碼的差值
那么,問題來了,前面我們比較的只有兩個對象,如果需要比較多個對象呢,改怎么辦呢?這時候就需要用到數組來存放對象,用Arrays里的排序方法進行排序
當我們寫下以下代碼:
public class Student {private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
}
public class Test {public static void main(String[] args) {Student[] students = new Student[]{new Student("zhangsan",18),new Student("lisi", 10),new Student("wangwu", 20)};System.out.println("排序前:" + Arrays.toString(students));Arrays.sort(students);System.out.println("排序后:" + Arrays.toString(students));}
}
當我們運行代碼后,發現會出現異常
這里需要用到強轉,而Student類并沒有去實現Comparable接口,因此會導致強轉失敗!這就是異常所在!所以,我們需要在Student類中實現Comparable接口
public class Student implements Comparable<Student>{private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic int compareTo(Student o) {return this.age-o.age;}
}
當我們再次去運行代碼時,會發現排序后是根據年齡從小到大排序的,這和Student類中實現的compareTo難道有關系?答案是正確的
假如我們想按照年齡從大到小去排序,就做出如下更改
調換順序即可,再次運行代碼:
假如想根據年齡去比呢?
public class Student implements Comparable<Student>{private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic int compareTo(Student o) {return this.name.compareTo(o.name);}
}
public class Test1 {public static void main(String[] args) {Student[] students = new Student[]{new Student("zhangsan",10),new Student("lisi", 18),new Student("wangwu", 9)};System.out.println("排序前:" + Arrays.toString(students));Arrays.sort(students);System.out.println("排序后:" + Arrays.toString(students));}
}
3. Comparator接口
在前面使用Comparable接口來實現對象的比較時,我們不難發現,這個比較方法并不靈活,只能固定地通過一種方式去比較,那么如果我們有的時候想通過年齡比較,有的時候想通過姓名比較,這個接口就無法實現了,這時地解決辦法就是,換一個接口,用Comparator接口來實現!
這時,我們分別寫兩個類——NameComparator和AgeComparator,代表分別通過姓名比較和通過年齡比較。并且這兩個類都要實現Comparator接口!
import java.util.Comparator;public class NameComparator implements Comparator<Student> {@Overridepublic int compare(Student o1, Student o2) {return o1.getName().compareTo(o2.getName());//姓名通過compareTo方法進行比較}
}
import java.util.Comparator;public class AgeComparator implements Comparator<Student> {@Overridepublic int compare(Student o1, Student o2) {return o1.getAge() - o2.getAge();}
}
注意: 實現Comparator接口需要重寫的時compare方法,不是compareTo方法!實現Comparable接口重寫的才是compareTo方法!
學生類如下:
public class Student {private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
}
測試一下代碼:
import java.util.Arrays;public class Test {public static void main(String[] args) {//兩個對象的比較AgeComparator ageComparator = new AgeComparator();NameComparator nameComparator = new NameComparator();Student student1 = new Student("zhangsan", 20);Student student2 = new Student("lisi", 30);System.out.println("根據年齡比:" + ageComparator.compare(student1, student2));System.out.println("根據姓名比:" + nameComparator.compare(student1, student2));//一組對象的比較Student[] students = new Student[]{new Student("zhangsan", 10),new Student("lisi", 18),new Student("wangwu", 9)};System.out.println("排序前:" + Arrays.toString(students));Arrays.sort(students, ageComparator);System.out.println("根據年齡排序后:" + Arrays.toString(students));Arrays.sort(students, nameComparator);System.out.println("根據姓名排序后:" + Arrays.toString(students));}
}
分析兩個對象的比較:
這里,我們創建了一個AgeComparator和一個NameComparator對象,用來表示是通過年齡比較還是通過姓名比較。注意最后兩個Student類對象的比較方法
分析一組對象的比較:
我們發現,在用Arrays.sort排序時,里面還加上了一個ageComparator對象(或NameComparator對象),通過這個對象,可以控制通過什么方式去比較,當使用Arrays對數組進行排序時,就會調用ageComparator(或NameComparator)里的compare方法依次將數組里的對象進行比較
最后,運行結果: