TreeSet是實現Set接口的實現類。因此它存儲的值是惟一的,同時也能夠對存儲的值進行排序,排序用的是二叉樹原理。因此要理解這個類,必須先簡單理解一下什麼是二叉樹。java
假若有這麼一個集合TreeSet<Integer>是[5,11,6,5,23,14]ide
用二叉樹是怎麼排序的呢?函數
二叉樹遍歷方法比較多,有興趣本身百度看下吧。這裏只須要知道元素是怎麼插入到二叉樹便可。小的存儲在左邊(負數),大的存儲在右邊(正數),相等不存儲。this
@Test public void test() { Set<Integer> set = new TreeSet<Integer>(); set.add(2); set.add(22); set.add(24); set.add(2); for (int i : set) { System.out.println(i); } }
輸出結果spa
2 22 24
能夠知道,TreeSet集合不只能夠保證集合元素的惟一性,還能夠排序。code
若是TreeSet裏面存儲的是對象呢?會出現什麼狀況呢?對象
public static void main(String[] args) { User u1 = new User("Kevin", 72); User u2 = new User("Jack", 22); User u3 = new User("Erik", 32); User u4 = new User("Blank", 72); Set<User> set = new TreeSet<User>(); set.add(u1); set.add(u2); set.add(u3); set.add(u4); for (User u : set) { System.out.println(u); } }
輸出結果blog
Exception in thread "main" java.lang.ClassCastException: com.kevin.exercise10.User cannot be cast to java.lang.Comparable at java.util.TreeMap.put(TreeMap.java:542) at java.util.TreeSet.add(TreeSet.java:238) at com.kevin.exercise10.TreeSetTest.main(TreeSetTest.java:23)
報錯了,由於集合裏面的是對象,對象不能轉換爲比較可比較對象。排序
若是想根據年齡排序,打印出各個對象(toString方法),應該怎麼作呢?在API裏面搜索一下Comparable,發現是個接口,那麼咱們就可讓Students類實現Comparable接口方法,這樣Students對象就成爲了可比較對象了。接口
User類實現Comparable接口方法:
public class User implements Comparable<User> { private String name; private int age; @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + 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; } @Override public int compareTo(User u) { int num = this.age - u.age; return this.age - u.age; } public User(String name, int age) { this.name = name; this.age = age; } public User() { } }
爲何是this.age-u.age?this.age表明調用時的對象的age,返回的若是是正數(比u.age大),就存儲在右邊。返回的是若是是負數(比u.age小),就存儲在左邊。若是等於0,就不存儲。
這就出問題了,若是兩我的不一樣名字,一樣年齡,this.age - o.age = 0,不就存不進二叉樹了嗎?
驗證一下:
public static void main(String[] args) { User u1 = new User("Kevin", 72); User u2 = new User("Jack", 22); User u3 = new User("Erik", 32); User u4 = new User("Blank", 72); Set<User> set = new TreeSet<User>(); set.add(u1); set.add(u2); set.add(u3); set.add(u4); for (User u : set) { System.out.println(u); } }
輸出結果:
User{name='Jack', age=22} User{name='Erik', age=32} User{name='Kevin', age=72}
因此須要改進一下User類的compareTo方法,保證同年齡,可是不一樣名字的學生也能存進二叉樹。
@Override public int compareTo(User u) { int num = this.age - u.age; return num == 0 ? this.name.compareTo(u.name) : num; }
這樣就能夠把四個不一樣的對象存儲進來,而且先按照年齡排序,年齡相同的再按照字符串排序。
輸出結果:
User{name='Jack', age=22} User{name='Erik', age=32} User{name='Blank', age=72} User{name='Kevin', age=72}
---------------------------------------------------------------------------------------
除了這種方式能夠實現排序之外,還有一種方式能夠實現排序。
TreeSet有這麼一個構造方法:
TreeSet(Comparator<? super E> comparator) 構造一個新的,空的樹集,根據指定的比較器進行排序。
Comparator是什麼呢?API文檔看一下:Interface Comparator<T>,是一個接口,裏面有一個要實現的接口方法:int compare(T o1, T o2) 比較其兩個參數的順序。
例如,咱們要對字符串的長度進行排序,長度相同的安裝字符串排序:
public class TreeSetTest2 { public static void main(String[] args) { TreeSet<String> set = new TreeSet<String>(new SortedByLen()); set.add("abc"); set.add("abcd"); set.add("b"); set.add("a"); set.add("abab"); System.out.println(set); } } class SortedByLen implements Comparator<String> { @Override public int compare(String s1, String s2) { int num = s1.length() - s2.length(); return num == 0 ? s1.compareTo(s2) : num; } }
須要注意的是重寫compare方法的o1,o2。o1表明調用的對象,o2表明集合中的對象。兩種實現排序方式視狀況而用。
(1)天然順序(Comparable)
- TreeSet類的add()方法中會把存入的對象提高爲Comparable類型
- 調用對象的compareTo()方法和集合中的對象比較
- 根據compareTo()方法返回的結果進行存儲
(2)比較器順序(Comparator)
- 建立TreeSet的時候能夠指定一個Comparator
- 若是傳入了Comparator的子類對象,那麼TreeSet就會按照比較器中的順序排序
- 調用的對象是compare方法的第一個參數,集合中的對象是compare方法的第二個參數
(3)兩種方式的區別
- TreeSet構造函數什麼都不傳,默認按照類中Comparable的順序(沒有就報錯ClassCastException)
- TreeSet若是傳入Comparator,就優先按照Comparator
若是不想保證元素的惟一性,改一下compare方法就能夠了,永遠不要讓它返回0。