Java深入了解TreeSet


Java中的TreeSet是Set的一個子類,TreeSet集合是用來對象元素進行排序的,同樣他也可以保證元素的唯一。
那TreeSet為什么能保證元素唯一,它是怎樣排序的呢?先看一段代碼:

public static void demo() {
        TreeSet<Person> ts = new TreeSet<>();
        ts.add(new Person("張三", 23));
        ts.add(new Person("李四", 13));
        ts.add(new Person("周七", 13));
        ts.add(new Person("王五", 43));
        ts.add(new Person("趙六", 33));
        
        System.out.println(ts);
    }

 

執行結果:

出錯,會拋出一個異常:java.lang.ClassCastException
顯然是出現了類型轉換異常。原因在於我們需要告訴TreeSet如何來進行比較元素,如果不指定,就會拋出這個異常


如何解決:
如何指定比較的規則,需要在自定義類(Person)中實現```Comparable```接口,並重寫接口中的compareTo方法

public class Person implements Comparable<Person> {
    private String name;
    private int age;
    ...
    public int compareTo(Person o) {
        return 0;                //當compareTo方法返回0的時候集合中只有一個元素
        return 1;                //當compareTo方法返回正數的時候集合會怎么存就怎么取
        return -1;                //當compareTo方法返回負數的時候集合會倒序存儲
    }
}


為什么返回0,只會存一個元素,返回-1會倒序存儲,返回1會怎么存就怎么取呢?原因在於TreeSet底層其實是一個二叉樹機構,且每插入一個新元素(第一個除外)都會調用```compareTo()```方法去和上一個插入的元素作比較,並按二叉樹的結構進行排列。
1. 如果將```compareTo()```返回值寫死為0,元素值每次比較,都認為是相同的元素,這時就不再向TreeSet中插入除第一個外的新元素。所以TreeSet中就只存在插入的第一個元素。
2. 如果將```compareTo()```返回值寫死為1,元素值每次比較,都認為新插入的元素比上一個元素大,於是二叉樹存儲時,會存在根的右側,讀取時就是正序排列的。
3. 如果將```compareTo()```返回值寫死為-1,元素值每次比較,都認為新插入的元素比上一個元素小,於是二叉樹存儲時,會存在根的左側,讀取時就是倒序序排列的。

利用上述規則,我們就可以按照年齡來排序了。代碼如圖

public int compareTo(Person o) {
        int num = this.age - o.age;                //年齡是比較的主要條件
        return num == 0 ? this.name.compareTo(o.name) : num;//姓名是比較的次要條件
    }

 

按照姓名排序(依據Unicode編碼大小),代碼如下:

public int compareTo(Person o) {
        int num = this.name.compareTo(o.name);        //姓名是主要條件
        return num == 0 ? this.age - o.age : num;    //年齡是次要條件
    }


按照姓名長度排序,代碼如下:

public int compareTo(Person o) {
        int length = this.name.length() - o.name.length();                //比較長度為主要條件
        int num = length == 0 ? this.name.compareTo(o.name) : length;    //比較內容為次要條件
        return num == 0 ? this.age - o.age : num;                        //比較年齡為次要條件
    }


以上是TreeSet如何比較自定義對象,接下來我們再來看一下TreeSet如何利用比較器比較元素。

需求:現在要制定TreeSet中按照String長度比較String。

//定義一個類,實現Comparator接口,並重寫compare()方法,
class CompareByLen /*extends Object*/ implements Comparator<String> {

    @Override
    public int compare(String s1, String s2) {        //按照字符串的長度比較
        int num = s1.length() - s2.length();        //長度為主要條件
        return num == 0 ? s1.compareTo(s2) : num;    //內容為次要條件
    }
}

 

    public static void demo4() {

        //需求:將字符串按照長度排序
        TreeSet<String> ts = new TreeSet<>(new CompareByLen());        //Comparator c = new CompareByLen();
        ts.add("aaaaaaaa");
        ts.add("z");
        ts.add("wc");
        ts.add("nba");
        ts.add("cba");
        
        System.out.println(ts);
    }

 

總結

  1. 特點
    • TreeSet是用來排序的, 可以指定一個順序, 對象存入之后會按照指定的順序排列
  2. 使用方式
    • a.自然順序(Comparable)
      • TreeSet類的add()方法中會把存入的對象提升為Comparable類型
      • 調用對象的compareTo()方法和集合中的對象比較
      • 根據compareTo()方法返回的結果進行存儲
    • b.比較器順序(Comparator)
      • 創建TreeSet的時候可以制定 一個Comparator
      • 如果傳入了Comparator的子類對象, 那么TreeSet就會按照比較器中的順序排序
      • add()方法內部會自動調用Comparator接口中compare()方法排序
      • 調用的對象是compare方法的第一個參數,集合中的對象是compare方法的第二個參數
    • c.兩種方式的區別
      • TreeSet構造函數什么都不傳, 默認按照類中Comparable的順序(沒有就報錯ClassCastException)
      • TreeSet如果傳入Comparator, 就優先按照Comparator


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM