考慮實(shí)現(xiàn)Comparable接口

什么是Comparable接口?

Comparable接口一般用于表示某個(gè)實(shí)例具有內(nèi)在的排序關(guān)系。

為什么需要實(shí)現(xiàn)Comparable接口?

我們知道,對(duì)于普通的數(shù)值或者字符串,都可以進(jìn)行一定的排序。但是,能不能直接給對(duì)象進(jìn)行排序呢?答案當(dāng)然是不能的了,實(shí)際上,之所以我們可以對(duì)數(shù)值和字符串進(jìn)行排序,是因?yàn)橄到y(tǒng)內(nèi)部已經(jīng)為我們定義了數(shù)值和字符串的排序關(guān)系。而我們定義的對(duì)象,本身是不包含排序關(guān)系的,因此,我們無(wú)法直接對(duì)對(duì)象進(jìn)行排序。

因此,如果我們需要對(duì)對(duì)象進(jìn)行排序的話,就必須定義對(duì)象的內(nèi)在排序關(guān)系,即實(shí)現(xiàn)Comparable接口。

如何實(shí)現(xiàn)Comparable接口?

我們舉個(gè)具體的例子,我們定義一個(gè)person對(duì)象,要求person按照年齡來(lái)進(jìn)行排序。

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
}

對(duì)于,上面的對(duì)象,我們是不能直接進(jìn)行排序的,必須要實(shí)現(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 / 

為了方便測(cè)試,我們重寫了toString方法,然后調(diào)用Collections.sort()方法來(lái)對(duì)list進(jìn)行排序。

現(xiàn)在我們重點(diǎn)來(lái)看實(shí)現(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方法返回三個(gè)結(jié)果,1,0,-1,那么它們分別代表什么意思呢?

  • 首先我們要知道我們比較的是this對(duì)象( 當(dāng)前對(duì)象 )和被比較的對(duì)象。
  • 其中,0表示兩者相等
  • -1表示當(dāng)前對(duì)象排在被比較對(duì)象之前
  • 1表示當(dāng)前對(duì)象排在被比較對(duì)象之后

我們上面實(shí)現(xiàn)的是對(duì)person中的age升序排列,因?yàn)楫?dāng)this.age<o.age時(shí),返回的是-1,表示this對(duì)象排在對(duì)象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;
    }

實(shí)際上,只要修改下比較號(hào)即可。

多重比較

對(duì)于,上面的Person類,可能出現(xiàn)age相同的情況,如果我還想繼續(xù)排序,假設(shè)age相同時(shí),對(duì)name進(jìn)行排序。那么這種情況又應(yīng)該如何實(shí)現(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 / 

我們可以看到,當(dāng)age相同時(shí),Mark,Ruby和Tom按照名字中的字母進(jìn)行排序。達(dá)到了我們需要的效果,我們具體來(lái)看下是如何實(shí)現(xiàn)的:

    @Override
    public int compareTo(Person o) {
        if (age > o.age)
            return 1;
        if (age < o.age)
            return -1;
        //若年齡相等,則直接通過(guò)名字來(lái)進(jìn)行排序。
        return name.compareTo(o.name);
    }

實(shí)際上,也就是當(dāng)年齡相等時(shí),調(diào)用name的compare方法來(lái)進(jìn)行排序。為什么這里可以直接調(diào)用compare方法呢?因?yàn)樽址谙到y(tǒng)中已經(jīng)設(shè)置好了內(nèi)部排序。這里默認(rèn)為升序,那如果需要設(shè)置降序呢?

return -name.compareTo(o.name);

修改為其相反數(shù)即可。

再度升級(jí),如果為Person類添加一個(gè)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相等的情況下,對(duì)age進(jìn)行排序
        if (age > o.age)
            return -1;
        if (age < o.age)
            return 1;
        //若id和age相等,則直接通過(guò)名字來(lái)進(jìn)行排序。
        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進(jìn)行升序排序,當(dāng)id相同時(shí),根據(jù)age進(jìn)行降序排序,如Marry和Tom,若id和age都相同,則對(duì)name進(jìn)行升序排序,如Mark和Ruby。

實(shí)現(xiàn)Comparable接口所需滿足的需求:
  • 滿足對(duì)稱性:必須確保所有的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));
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • compareTo方法并沒有在Object中聲明。相反,它是Comparable接口中唯一的一個(gè)方法。compar...
    郭_4d5f閱讀 763評(píng)論 0 1
  • 第十二條: 考慮實(shí)現(xiàn)Comparable接口 (注意本文不適合學(xué)習(xí),純屬個(gè)人筆記) 1. Comparable接口...
    想飛的僵尸閱讀 1,228評(píng)論 0 0
  • 項(xiàng)目中經(jīng)常會(huì)遇到列表搜索查詢,大部分的查詢是可以通過(guò)sql語(yǔ)句來(lái)實(shí)現(xiàn)的,有些特殊的搜索排序sql則實(shí)現(xiàn)不了,例如中...
    信徒_allen閱讀 2,601評(píng)論 0 1
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 31,759評(píng)論 18 399
  • 一轉(zhuǎn)眼國(guó)慶節(jié)假期過(guò)去好幾天了,說(shuō)好的要出去玩的,可是那幾天天又下雨了,就算不下雨估計(jì)外面出行也是擁堵的,倒不是去看...
    秋落塵閱讀 221評(píng)論 0 2