什么是Comparable接口赠摇?
Comparable接口一般用于表示某個實例具有內(nèi)在的排序關(guān)系。
為什么需要實現(xiàn)Comparable接口?
我們知道静檬,對于普通的數(shù)值或者字符串宽档,都可以進行一定的排序尉姨。但是,能不能直接給對象進行排序呢吗冤?答案當然是不能的了又厉,實際上,之所以我們可以對數(shù)值和字符串進行排序椎瘟,是因為系統(tǒng)內(nèi)部已經(jīng)為我們定義了數(shù)值和字符串的排序關(guān)系覆致。而我們定義的對象,本身是不包含排序關(guān)系的降传,因此篷朵,我們無法直接對對象進行排序。
因此婆排,如果我們需要對對象進行排序的話声旺,就必須定義對象的內(nèi)在排序關(guān)系,即實現(xiàn)Comparable接口段只。
如何實現(xiàn)Comparable接口腮猖?
我們舉個具體的例子,我們定義一個person對象赞枕,要求person按照年齡來進行排序澈缺。
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
對于,上面的對象炕婶,我們是不能直接進行排序的姐赡,必須要實現(xiàn)其Comparable接口:
public class Person implements Comparable<Person>{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person o) {
if (this.age > o.age)
return 1;
else if (this.age < o.age)
return -1;
else return 0;
}
@Override
public String toString() {
return "name: " + name + " age: " + age;
}
public static void main(String[] args){
List<Person> list = new ArrayList<>();
list.add(new Person("John",18));
list.add(new Person("Marry",21));
list.add(new Person("Tom",20));
System.out.println("Before sort:");
printList(list);
Collections.sort(list);
System.out.println("After sort:");
printList(list);
}
public static void printList(List<Person> list){
for (Person p : list){
System.out.print(p + " / ");
}
System.out.println();
}
}
輸出結(jié)果:
Before sort:
name: John age: 18 / name: Marry age: 21 / name: Tom age: 20 /
After sort:
name: John age: 18 / name: Tom age: 20 / name: Marry age: 21 /
為了方便測試,我們重寫了toString方法柠掂,然后調(diào)用Collections.sort()方法來對list進行排序项滑。
現(xiàn)在我們重點來看實現(xiàn)Comparable接口中的CompareTo方法:
@Override
public int compareTo(Person o) {
if (this.age > o.age)
return 1;
else if (this.age < o.age)
return -1;
else return 0;
}
CompareTo方法返回三個結(jié)果,1,0涯贞,-1枪狂,那么它們分別代表什么意思呢危喉?
- 首先我們要知道我們比較的是this對象( 當前對象 )和被比較的對象。
- 其中州疾,0表示兩者相等
- -1表示當前對象排在被比較對象之前
- 1表示當前對象排在被比較對象之后
我們上面實現(xiàn)的是對person中的age升序排列辜限,因為當this.age<o.age
時,返回的是-1严蓖,表示this對象排在對象o前面薄嫡。也就是較小的數(shù)排在前面。那么如果我們要改為降序呢谈飒?
@Override
public int compareTo(Person o) {
if (this.age < o.age)
return 1;
else if (this.age > o.age)
return -1;
else return 0;
}
實際上岂座,只要修改下比較號即可。
多重比較
對于杭措,上面的Person類费什,可能出現(xiàn)age相同的情況,如果我還想繼續(xù)排序手素,假設(shè)age相同時鸳址,對name進行排序。那么這種情況又應(yīng)該如何實現(xiàn)呢泉懦?
public class Person implements Comparable<Person>{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person o) {
if (age > o.age)
return 1;
if (age < o.age)
return -1;
return name.compareTo(o.name);
}
@Override
public String toString() {
return "name: " + name + " age: " + age;
}
public static void main(String[] args){
List<Person> list = new ArrayList<>();
list.add(new Person("John",18));
list.add(new Person("Marry",21));
list.add(new Person("Tom",20));
list.add(new Person("Mark",20));
list.add(new Person("Ruby",20));
System.out.println("Before sort:");
printList(list);
Collections.sort(list);
System.out.println("After sort:");
printList(list);
}
public static void printList(List<Person> list){
for (Person p : list){
System.out.print(p + " / ");
}
System.out.println();
}
}
輸出結(jié)果:
Before sort:
name: John age: 18 / name: Marry age: 21 / name: Tom age: 20 / name: Mark age: 20 / name: Ruby age: 20 /
After sort:
name: John age: 18 / name: Mark age: 20 / name: Ruby age: 20 / name: Tom age: 20 / name: Marry age: 21 /
我們可以看到稿黍,當age相同時,Mark崩哩,Ruby和Tom按照名字中的字母進行排序巡球。達到了我們需要的效果,我們具體來看下是如何實現(xiàn)的:
@Override
public int compareTo(Person o) {
if (age > o.age)
return 1;
if (age < o.age)
return -1;
//若年齡相等邓嘹,則直接通過名字來進行排序酣栈。
return name.compareTo(o.name);
}
實際上,也就是當年齡相等時汹押,調(diào)用name的compare方法來進行排序矿筝。為什么這里可以直接調(diào)用compare方法呢?因為字符串在系統(tǒng)中已經(jīng)設(shè)置好了內(nèi)部排序棚贾。這里默認為升序窖维,那如果需要設(shè)置降序呢?
return -name.compareTo(o.name);
修改為其相反數(shù)即可妙痹。
再度升級铸史,如果為Person類添加一個id屬性,并要求先按id升序怯伊,若id相同沛贪,則按年齡降序,若id和age都相同,則按name升序利赋。
public class Person implements Comparable<Person>{
private int id;
private String name;
private int age;
public Person(int id,String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person o) {
if (id > o.id)
return 1;
if (id < o.id)
return -1;
//id相等的情況下,對age進行排序
if (age > o.age)
return -1;
if (age < o.age)
return 1;
//若id和age相等猩系,則直接通過名字來進行排序媚送。
return name.compareTo(o.name);
}
@Override
public String toString() {
return "id: " + id + " name: " + name + " age: " + age;
}
public static void main(String[] args){
List<Person> list = new ArrayList<>();
list.add(new Person(3,"John",18));
list.add(new Person(2,"Marry",21));
list.add(new Person(2,"Tom",20));
list.add(new Person(4,"Mark",20));
list.add(new Person(4,"Ruby",20));
System.out.println("Before sort:");
printList(list);
Collections.sort(list);
System.out.println("After sort:");
printList(list);
}
public static void printList(List<Person> list){
for (Person p : list){
System.out.print(p + " / ");
}
System.out.println();
}
}
輸出結(jié)果:
Before sort:
id: 3 name: John age: 18 / id: 2 name: Marry age: 21 / id: 2 name: Tom age: 20 / id: 4 name: Mark age: 20 / id: 4 name: Ruby age: 20 /
After sort:
id: 2 name: Marry age: 21 / id: 2 name: Tom age: 20 / id: 3 name: John age: 18 / id: 4 name: Mark age: 20 / id: 4 name: Ruby age: 20 /
我們可以看到首先根據(jù)id進行升序排序,當id相同時寇甸,根據(jù)age進行降序排序塘偎,如Marry和Tom,若id和age都相同拿霉,則對name進行升序排序吟秩,如Mark和Ruby。
實現(xiàn)Comparable接口所需滿足的需求:
- 滿足對稱性:必須確保所有的x和y都滿足sgn(x.compareTo(y)) == -sgn(y.compareTo(x));
- 滿足傳遞性:若(x.compareTo(y) >0 && y.compareTo(z)>0)绽淘,則x.compareTo(z)>0涵防。
- 若x.compareTo(y)==0,則sgn(x.compareTo(z)) == sgn(y.compareTo(z));