TreeSet集合解析

  TreeSet是實現Set接口的實現類。因此它存儲的值是惟一的,同時也能夠對存儲的值進行排序,排序用的是二叉樹原理。因此要理解這個類,必須先簡單理解一下什麼是二叉樹。java

二叉樹原理簡析

  假若有這麼一個集合TreeSet<Integer>是[5,11,6,5,23,14]ide

  用二叉樹是怎麼排序的呢?函數

  二叉樹遍歷方法比較多,有興趣本身百度看下吧。這裏只須要知道元素是怎麼插入到二叉樹便可。小的存儲在左邊(負數),大的存儲在右邊(正數),相等不存儲。this

TreeSet的基本使用

@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。

 

 

 

 

原文參考【https://www.javazhiyin.com/

相關文章
相關標籤/搜索