請排隊


Collection 中談到TreeSet,會將你加入的物件進行排序,預設使用自然順序(Natural order)。例如:
Set set = new TreeSet();
set.add("Justin");
set.add("Bush");
set.add("Momor");
System.out.println(set);

最後顯示的順序是[Bush, Justin, Momor]。如果你定義了Student類別:
public class Student {
    private String name;
    private int score;
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    public String getName() {
        return name;
    }
    public int getScore() {
        return score;
    }
    public String toString() {
        return "Student [name=" + name + ", score=" + score + "]";
    }
}

然後這麼新增至TreeSet:
Set set = new TreeSet();
set.add(new Student("Justin", 96));
set.add(new Student("Bush", 88));
set.add(new Student("Momor", 93));
System.out.println(set);

那在第二次新增Student實例時會引發ClassCastException的錯誤,先不論引發例外的原因為何,你並沒有告訴TreeSet,是要根據學生名稱,還是根據學生分數排序不是嗎?

可以試著讓Student實作Comparable介面:
public class Student implements Comparable<Student> {
    private String name;
    private int score;
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    public String getName() {
        return name;
    }
    public int getScore() {
        return score;
    }
    public String toString() {
        return "Student [name=" + name + ", score=" + score + "]";
    }
    public int compareTo(Student other) {
        return this.score - other.score;
    }
}

Comparable有個compareTo()方法要實作,它會傳 入一個物件,你要傳回大於0、0或小於0的數。傳回大於0表示,傳入的物件順序上比目前物件大,應該排在後面,傳回0表示兩個物件順序上相等,傳回小於0 表示傳入的物件順序上比目前物件小,應該排在前面。就上例而言,會根據學生的分數由小到大排序。重新執行上面新增學生的程式片段,結果會顯示 [Student [name=Bush, score=88], Student [name=Momor, score=93], Student [name=Justin, score=96]]。

所以,當你使用TreeSet新增物件時,TreeSet會預期新增的物件都必須實作Comparable介面,在嘗試比較兩個物件時,其實就會試著將新 增的物件轉型(Cast)為Comparable型態,再用compareTo()進行比較。所以先前未實作Comparable的Student在新增 至TreeSet時,就會引發ClassCastException,原因就在此。

排序時根據物件上的Comparable介面定義之順序,就稱之自然順序(Natural order)。事實上,String就實作了Comparable介面,所以你可以直接將字串加入TreeSet,就是這個原因。

然而有時候,你並不是有辦法讓物件實作Comparable介面,例如String就是了,你不能改變String,也不能繼承String並實作 Comparable介面,若你想讓TreeSet排序字串時以與先前相反的順序該如何?你可以在建立TreeSet時,指定一個Comparator實 作物件。例如:
Set set = new TreeSet(new Comparator<String>() {
            public int compare(String s1, String s2) {
                return -s1.compareTo(s2);
            }
        });
set.add("Justin");
set.add("Bush");
set.add("Momor");
System.out.println(set);

Comparator有一個compare()方法要實作,它會傳入兩個物件,同樣地,你要傳回你要傳回大於0、0或小於0的數。傳回大於0表示,左邊傳入的物件順序上比右邊傳入物件大,應該排在後面,傳回0表示兩個物件順序上相等,傳回小於0表示左邊傳入的物件順序上比右邊傳入物件小,應該排在前面。上例會顯示[Momor, Justin, Bush]。

如果你不想用TreeSet排序,則可以將物件新增至List,再使用Collections的sort()方法進行排序。你可以使用自然順序。例如以下顯示[Bush, Justin, Momor]:
List<String> list = new ArrayList<String>();
list.add("Justin");
list.add("Bush");
list.add("Momor");
Collections.sort(list);
System.out.println(list);

或者是給定一個Comparator,例如以下顯示[Momor, Justin, Bush]:
List<String> list = new ArrayList<String>();
list.add("Justin");
list.add("Bush");
list.add("Momor");
Collections.sort(list, new Comparator<String>() {
            public int compare(String s1, String s2) {
                return -s1.compareTo(s2);
            }          
        });
System.out.println(list);