Comparable和Comparator的用法

Java 中基本類型的比較可以使用比較運算符,不同於C++,Java 中的對象比較是不能利用運算符重載(儘管 Java 中有運算符重載現象)。Comparable 和 Comparator 接口的存在就是爲了對象比較,我們可以在接口中定義對象比較規則,還可以利用 Collections.sort 和 Arrays.sort 方法對對象數組和集合進行排序。

Comparable接口

該接口位於 java.lang 包下,接口中只有一個抽象方法 compareTo(),下面就是接口的源碼 :

package java.lang;
import java.util.*;
public interface Comparable<T> {
    public int compareTo(T o);
}

對於實現該接口的類,會被強加一個整體排序,排序規則在 compareTo 方法中進行定義,也稱這種排序方式爲自然排序

怎麼使用這個接口呢?這裏通過一個相親的例子看我們如何通過 Comparable 接口給相親對象做一個簡單排序。一般相親,女方會看男方有沒有錢,年齡有多大等等之類的,當然,也不排除男方會在意女方有沒有錢……

首先,我們定義一個 Person 類並實現 Comparable 接口:

public class Person implements Comparable<Person>{
	String name;
	int age;
	int money;
	public Person(String name, int age, int money) {
		this.name = name;
		this.age = age;
		this.money = money;
	}
	@Override
	public int compareTo(Person another) {
		//這裏我們選擇用金錢來對這個相親對象進行排序,還是比較現實的哈
		if(this.money > another.money)
			return 1;
		else if(this.money == another.money)
			return 0;
		else
			return -1;
	}
}

Tips:Comparable 泛型接口中的 T 在使用時,須指明爲要比較的對象類型,例如 Person

在 Person 類中,我們已經定義好了規則,假設只有兩個相親對象 Jack 和 Timi,可以直接通過 compareTo 這個方法就可以完成比較:

public class Test {
	public static void main(String[] args) {
		Person Jack = new Person("Jack", 25, 3000000);
		Person Timi = new Person("Timi", 20, 2000000);
		if(Timi.compareTo(Jack) > 0)
			System.out.println("My choice is\r" + Timi.name);
		else
			System.out.println("My choice is\r" + Jack.name);
	}
}/*output:
My choice is
Jack
*/

Tips:自然排序中的 a.compareTo(b) == 0 等價於 a.equals(b),但是 compareTo 中不允許比較 null ,否則會拋出空指針異常。

在比較規則下,當然是 Jack 比 Timi 有錢,選 Jack 沒煩惱;假設相親對象不止一個,大約有一個數組那麼多,可以使用 Arrays.sort 進行排序:

public class Test {
	public static void main(String[] args) {
		Person[] men = new Person[]{
			new Person("Jack",25,3000000),
			new Person("Tom",20,2000000),
			new Person("Siri",31,10000000),
			new Person("Nacy",26,2500000)
		};
		Arrays.sort(men);
		for(Person man:men) {
			System.out.println(man.name +"\t"+ man.age +"\t"+ man.money);
		}
	}
}/*output:
Tom 	20	2000000
Nacy	26	2500000
Jack	25	3000000
Siri	31	10000000
*/

如果不進行排序,相親對象在數組中就會雜亂不堪,使用 Arrays.sort(Object[] a) 方法進行排序後,對象們根據財力的從小到大進行了一個排序。假如我們想要將財力從大到小排序,只需要修改一個比較規則,即 compareTo 方法:

public int compareTo(Person another) {
	if(this.money < another.money)
		return 1;
	else if(this.money == another.money)
		return 0;
	else
		return -1;
}

因爲,Arrays.sort 只能進行對象數組的排序,如果相親對象是放在集合中,可以使用 Collections.sort(List list) 進行排序:

public class Test {
	public static void main(String[] args) {
		ArrayList<Person> men = new ArrayList<>();
		men.add(new Person("Jack",25,3000000));
		men.add(new Person("Tom",20,2000000));
		men.add(new Person("Siri",31,10000000));
		men.add(new Person("Nacy",26,2500000));
		Collections.sort(men);
		for(Person man:men) {
			System.out.println(man.name +"\t"+ man.age +"\t"+ man.money);
		}
	}
}

Tips:使用 Arrays.sort 和 Collections.sort 進行對象排序時,所要排序的對象必須實現了 Comparable 接口。

在 java.lang 包中,可以看到基本類型的包裝類們,都實現了 Comparable 接口,它們都定義了自己的比較規則。

Comparator接口

該接口位於 java.util 包下,這個接口中的方法還是比較多的,如果我們想要使用這個接口,只需要實現一個抽象方法 compare 就可以了:

public interface Comparator<T> {
	int compare(T o1, T o2);
}

對於實現該接口的類,我們稱爲比較器類,在使用 Arrays.sort 和 Collections.sort 方法進行排序時,只需要將要比較的對象和比較器傳入方法,在比較器的 compare 方法中定義了比較規則,我們也稱此排序方式爲比較器排序

下面就是使用比較器,對相親對象進行排序。首先,我們就是要刪除 Person 類中的比較規則:

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

然後,我們定義一個 Person 類的比較器,需要實現 Comparator 類:

public class PersonComparator implements Comparator<Person>{
	@Override
	public int compare(Person o1, Person o2) {
		if(o1.money > o2.money)
			return 1;
		else if(o1.money == o2.money)
			return 0;
		else
			return -1;
	}
}

最後,我們使用 Arrays.sort(T[] a, Comparator<? super T> c) 方法 和 Collections.sort(List list, Comparator<? super T> c) 方法給對象們進行一個財力排序:

public class Test {
	public static void main(String[] args) {
		/*Person[] men = new Person[]{
			new Person("Jack",25,3000000),
			new Person("Tom",20,2000000),
			new Person("Siri",31,10000000),
			new Person("Nacy",26,2500000)
		};*/
		ArrayList<Person> men = new ArrayList<>();
		men.add(new Person("Jack",25,3000000));
		men.add(new Person("Tom",20,2000000));
		men.add(new Person("Siri",31,10000000));
		men.add(new Person("Nacy",26,2500000));
//		Arrays.sort(men, new PersonComparator());
		Collections.sort(men, new PersonComparator());
		for(Person man:men) {
			System.out.println(man.name +"\t"+ man.age +"\t"+ man.money);
		}
	}
}/*output:
Tom 	20	2000000
Nacy	26	2500000
Jack	25	3000000
Siri	31	10000000
*/

如果我們僅僅對兩個相親對象就行比較,就不能用 compareTo 方法了,理智告訴我們,Person 類中已經沒有了比較規則。

那麼,如果我們想要對這兩個相親對象進行比較,而又不使用對象數組或者集合,我們又該怎麼實現?

public class Test {
	public static void main(String[] args) {
		Person Jack = new Person("Jack", 25, 3000000);
		Person Timi = new Person("Timi", 20, 2000000);
		PersonComparator comparator = new PersonComparator();
		if(comparator.compare(Jack, Timi) > 0) {
			System.out.println("My choice is\r" + Jack.name);
		}else {
			System.out.println("My choice is\r" + Timi.name);
		}
	}
}/*output:
My choice is
Jack
*/

如果查看 java.util 包中的集合類,我們會發現:集合類中的 sort 方法大都支持傳入一個自定義的比較器,來改變默認的排序方式。

總結

1. compareTo 方法或者 compare 方法的返回值,都不要求一定返回 -1, 0, 1 ;我們只需要返回一個 正整數、負整數或者 0 即可。

所以上面的 comparaTo 方法和 comparable 方法實際上可以寫成差值的形式:

public int compare(Person o1, Person o2) {
		return o1.money - o2.money;
}

2. 使用 Comparable 和 Comparator 接口時拋出空指針異常,並不代表我們不能比較空對象,只要我們在對應的方法中給出處理空對象的規則,同樣可以進行下去:

public int compareTo(Person another) {
	//一個不存在的相親對象肯定比不過這些實實在在的相親對象
	if(another == null)
		return -1;
	if(this.money > another.money)
		return 1;
	else if(this.money == another.money)
		return 0;
	else
		return -1;
}

我們可以簡單測試一下,空對象處理方法:

public class Test {
	public static void main(String[] args) {
		Person Jack = new Person("Jack", 25, 3000000);
		Person Timi = new Person("Timi", 20, 2000000);
		if(Timi.compareTo(null) > 0)
			System.out.println("My choice is\r null");
		else
			System.out.println("My choice is\r" + Jack.name);
	}
}/*output:
My choice is
Jack
*/

3. Comparable 和 Comparator 的區別:實現 Comparable 的類,說明這個類是可比較的;實現 Comparator 的類變成了一個工具類,需要比較的類可以使用這個工具類進行比較。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章