讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議81:非穩定排序推薦使用List >

建議81:非穩定排序推薦使用List

我們知道Set與List的最大區別就是Set中的元素不可以重複(這個重複指的equals方法的返回值相等),其他方面則沒有太大的區別了,在Set的實現類中有一個比較常用的類需要瞭解一下:TreeSet,該類實現了類默認排序為升序的Set集合,如果插入一個元素,默認會按照升序排列(當然是根據Comparable接口的compareTo的返回值確定排序位置了),不過,這樣的排序是不是在元素經常變化的場景中也適用呢?我們來看例子:


public static void main(Stringargs){

SortedSet<Person>set=new TreeSet<Person>();

//身高180CM

set.add(new Person(180));

//身高175CM

set.add(new Person(175));

for(Person p:set){

System.out.println("身高:"+p.getHeight());

}

}

static class Person implements Comparable<Person>{

//身高

private int height;

public Person(int_age){

height=_age;

}

/*height的getter/setter方法省略*/

//按照身高排序

public int compareTo(Person o){

return height-o.height;

}

}


這是set的簡單用法,定義一個Set集合,之後放入兩個元素,雖然175後放入,但是由於是按照升序排列的,所以輸出的結果應該是175在前,180在後,結果如下:


身高:175

身高:180


這沒有問題,隨著時間的推移,身高175cm的人長高了10cm,而180cm卻保持不變,那排序的位置應該改變一下吧,看代碼:


public static void main(Stringargs){

SortedSet<Person>set=new TreeSet<Person>();

//身高180CM

set.add(new Person(180));

//身高175CM

set.add(new Person(175));

//身高最矮的人大變身

set.frst().setHeight(185);

for(Person p:set){

System.out.println("身高:"+p.getHeight());

}

}


找出身高最矮的人,也就是排在第一個位的人,然後修改一下身高值,我們猜想一下輸出結果是什麼?重新排序了?看輸出:


身高:185

身高:180


很可惜,竟然沒有重新排序,偏離了我們的預期。這正是下面要說明的問題,SortedSet接口(TreeSet實現了該接口)只是定義了在給集合加入元素時將其進行排序,並不能保證元素修改後的排序結果,因此TreeSet適用於不變量的集合數據排序,比如String、Integer等類型,但不適用於可變量的排序,特別是不確定何時元素會發生變化的數據集合。

原因知道了,那如何解決此類重排序問題呢?有兩種方式:

(1)Set集合重排序

重新生成一個Set對象,也就是對原有的Set對像重排序,代碼如下:


public static void main(Stringargs){

SortedSet<Person>set=new TreeSet<Person>();

//身高180CM

set.add(new Person(180));

//身高175CM

set.add(new Person(175));

//身高最矮的人大變身

set.first().setHeight(185);

//set重排序

set=new TreeSet<Person>(new ArrayList<Person>(set));

}


就這一句話即可重新排序。可能有讀者會問,使用TreeSet(SortedSet<E>s)這個構造函數不是可以更好地解決問題嗎?不行,該構造函數只是原Set的淺拷貝,如果裡面有相同的元素,是不會重新排序的。

(2)徹底重構掉TreeSet,使用List解決問題

我們之所以使用TreeSet是希望實現自動排序,即使修改也能自動排序,既然它無法實現,那就用List來代替,然後再使用Collections.sort()方法對List排序,代碼較簡單,不再贅述。

兩種方法都可以解決我們的困境,到底哪一個是最優的呢?對於不變量的排序,例如直接量(也就是8個基本類型)、String類型等,推薦使用TreeSet,而對於可變量,例如我們自己寫的類,可能會在邏輯處理中改變其排序關鍵值的,則建議使用List自行排序。

又有問題了,如果需要保證集合中元素的唯一性,又要保證元素值修改後排序正確,那該如何處理呢?List不能保證集合中的元素唯一,它是可以重複的,而Set能保證元素唯一,不重複。如果採用List解決排序問題,就需要自行解決元素重複問題(若要剔除也很簡單,轉變為HashSet,剔除後再轉回來)。若採用TreeSet,則需要解決元素修改後的排序問題,孰是孰非,就需要根據具體的開發場景來決定了。

注意 SortedSet中的元素被修改後可能會影響其排序位置。