天天看點

淺析 Comparable和 Comparator的差別

簡介

Comparable和 Comparator都是java.util包下的兩個接口,從字面上看這兩個接口都是用來做比較用的,但是jdk裡面不可能定義兩個功能相同的接口,是以他們肯定有不同的用處。

1、Comparable

1.1 說明

Comparable可以認為是一個内比較器,實作了Comparable接口的類有一個特點,就是這些 類是可以和自己比較的,至于具體和另一個實作了Comparable接口的類如何比較,則依賴compareTo方法的實作,compareTo方法也被稱為自然比較方法。如果開發者add進入一個Collection的對象想要Collections的sort方法幫你自動進行排序的話,那麼這個對象必須實作Comparable接口。compareTo方法的傳回值是int,有三種情況:

1、比較者大于被比較者(也就是compareTo方法裡面的對象),那麼傳回正整數
2、比較者等于被比較者,那麼傳回0
3、比較者小于被比較者,那麼傳回負整數
           

1.2 舉例

1.定義一個Girl.java類,實作Comparable接口,并且重寫compareTo方法:預設比較的是目前Girl類,通過age屬性進行比較。

public class Girl implements Comparable<Object> {

	private String name;
	private int age;

	public String getName() {
    	return name;
	}

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public String toString() {
        return "Girl [name=" + name + ", age=" + age + "]";
    }

    @Override
    public int compareTo(Object o) {
        Girl g = (Girl)o;
        return this.age - g.getAge();
    }

}
           

2.定義測試類Test.java,定義一個ArrayList儲存75個Girl,打亂順序後(因為List本身是有序的),檢視輸出結果。代碼如下:

public static void main(String[] args) {
        
        List<Girl> list = new ArrayList<>(100);
        Girl girl;
        for (int i=0; i<75; i++) {
            girl = new Girl("girl " + i, i);
            list.add(girl);
        }
        Collections.shuffle(list);
        list.stream().forEach(System.out::println);
        
    }
           

輸出結果:

Girl [name=girl 9, age=9]

Girl [name=girl 60, age=60]

Girl [name=girl 26, age=26]

Girl [name=girl 55, age=55]

Girl [name=girl 12, age=12]

Girl [name=girl 31, age=31]

Girl [name=girl 49, age=49]

Girl [name=girl 6, age=6]

輸出是亂序的

3、還是2中的方法,隻不過添加一行代碼:使用Collections的sort(T t)方法排序。

public static void main(String[] args) {
        
        List<Girl> list = new ArrayList<>(100);
        Girl girl;
        for (int i=0; i<75; i++) {
            girl = new Girl("girl " + i, i);
            list.add(girl);
        }
        Collections.shuffle(list);
        Collections.sort(list);
        list.stream().forEach(System.out::println);
        
    }
           

輸出結果:

Girl [name=girl 0, age=0]

Girl [name=girl 1, age=1]

Girl [name=girl 2, age=2]

Girl [name=girl 3, age=3]

Girl [name=girl 4, age=4]

Girl [name=girl 5, age=5]

Girl [name=girl 6, age=6]

Girl [name=girl 7, age=7]

Girl [name=girl 8, age=8]

Girl [name=girl 9, age=9]

Girl [name=girl 10, age=10]

Girl [name=girl 11, age=11]

輸出是有序的,并且是按compareTo方法中定義的根據age的升序比較。

2、Comparator

2.1 說明

Comparator可以認為是是一個外比較器,個人認為有兩種情況可以使用實作Comparator接口的方式:

1、一個對象不支援自己和自己比較(沒有實作Comparable接口),但是又想對兩個對象進行比較。

2、一個對象實作了Comparable接口,但是開發者認為compareTo方法中的比較方式并不是自己想要的那種比較方式。

Comparator接口裡面有一個compare方法,方法有兩個參數T o1和T o2,是泛型的表示方式,分别表示待比較的兩個對象,方法傳回值和Comparable接口一樣是int,有三種情況:

1、o1大于o2,傳回正整數
  2、o1等于o2,傳回0
  3、o1小于o3,傳回負整數
           

2.2 舉例

1.定義一個Girl.java類如下:

public class Girl {

    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public String toString() {
        return "Girl [name=" + name + ", age=" + age + "]";
    }

}
           

2.定義GirlComparator.java類,實作Comparator接口,重寫compare()方法,和前面一樣根據age進行比較。

public class GirlComparator implements Comparator<Girl> {

    @Override
    public int compare(Girl g1, Girl g2) {
        return g1.getAge() - g2.getAge();
    }
    
}
           

2.定義測試類Test.java,定義一個TreeSet儲存75個Girl,檢視輸出結果。代碼如下:

public static void main(String[] args) {

        Set<Girl> set = new TreeSet<>();
        Girl girl;
        for (int i = 0; i< 75; i++) {
            girl = new Girl("girl "+i, i);
            set.add(girl);
        }
        set.stream().forEach(System.out::println);
    
}  
           

輸出結果:

Girl [name=girl 0, age=0]

Girl [name=girl 1, age=1]

Girl [name=girl 2, age=2]

Girl [name=girl 3, age=3]

Girl [name=girl 4, age=4]

Girl [name=girl 5, age=5]

Girl [name=girl 6, age=6]

Girl [name=girl 7, age=7]

Girl [name=girl 8, age=8]

Girl [name=girl 9, age=9]

Girl [name=girl 10, age=10]

Girl [name=girl 11, age=11]

Girl [name=girl 12, age=12]

輸出是有序的,并且是按compare方法中定義的根據age的升序比較。

3、總結

總結一下,這兩種比較器Comparable和Comparator,後者相比前者有如下優點:

  • 個性化比較:如果實作類沒有實作Comparable接口,又想對兩個類進行比較(或者實作類實作了Comparable接口,但是對compareTo方法内的比較算法不滿意),那麼可以實作Comparator接口,自定義一個比較器,寫比較算法。
  • 解耦:實作Comparable接口的方式比實作Comparator接口的耦合性要強一些,如果要修改比較算法,要修改Comparable接口的實作類,而實作Comparator的類是在外部進行比較的,不需要對實作類有任何修改。從這個角度說,其實有些不太好,尤其在我們将實作類的.class檔案打成一個.jar檔案提供給開發者使用的時候。