詳解java中的TreeSet集合


TreeSet是實現Set接口的實現類。所以它存儲的值是唯一的,同時也可以對存儲的值進行排序,排序用的是二叉樹原理。所以要理解這個類,必須先簡單理解一下什么是二叉樹。

  • 二叉樹原理簡述

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

用二叉樹是怎么排序的呢?

二叉樹遍歷方法比較多,有興趣自己百度看下吧。這里只需要知道元素是怎么插入到二叉樹即可。小的存儲在左邊(負數),大的存儲在右邊(正數),相等不存儲。

 

  • TreeSet的基本使用

public static void main(String[] args) {
        TreeSet<Integer> ts = new TreeSet<>();
        ts.add(2);
        ts.add(1);
        ts.add(3);
        ts.add(2);
        ts.add(3);
        ts.add(1);
        ts.add(2);
        System.out.println(ts);
    }

// 輸出結果:
[1,2,3]

可以知道,TreeSet集合不僅可以保證集合元素的唯一性,還可以排序。

如果TreeSet里面存儲的是對象呢?會出現什么情況呢?

public static void main(String[] args) {
        TreeSet<Students> ts = new TreeSet<>();
        ts.add(new Students("張三",13));
        ts.add(new Students("李四",14));
        ts.add(new Students("王五",15));
        System.out.println(ts);
    }

// 輸出結果:
Exception in thread "main" java.lang.ClassCastException: com.lei.Students cannot be cast to java.lang.Comparable

報錯了,因為集合里面的是對象,對象不能轉換為比較可比較對象。

如果想根據年齡排序,打印出各個對象(toString方法),應該怎么做呢?

在API里面搜索一下Comparable,發現是個接口,那么我們就可以讓Students類實現Comparable接口方法,這樣Students對象就成為了可比較對象了。

Students類實現Comparable接口方法:

public class Students implements Comparable<Students> {
    private String name;
    private int age;

    ......

    @Override
    public int compareTo(Students o) {
        return this.age-o.age;
    }
}

為什么是this.age-o.age?this.age代表調用時的對象的age,返回的如果是正數(比o.age大),就存儲在右邊。返回的是如果是負數(比o.age小),就存儲在左邊。如果等於0,就不存儲。

這就出問題了,如果兩個人不同名字,同樣年齡,this.age - o.age = 0,不就存不進二叉樹了嗎?

驗證一下:

public static void main(String[] args) {
        TreeSet<Students> ts = new TreeSet<>();
        ts.add(new Students("李四",14));
        ts.add(new Students("張三",13));
        ts.add(new Students("王五",15));
        ts.add(new Students("趙六",13));
        System.out.println(ts);
    }

// 輸出結果只有張三、李四、王五

所以需要改進一下Students類的compareTo方法,保證同年齡,但是不同名字的學生也能存進二叉樹。

@Override
    public int compareTo(Students o) {
        int num = this.age - o.age;
        // String類里面已經重寫了compareTo方法
        // int    compareTo(String anotherString)  按字典順序比較兩個字符串
        return num == 0 ? this.name.compareTo(o.name) : num;

這樣就可以把四個不同的對象存儲進來,並且先按照年齡排序,年齡相同的再按照字符串排序。

 

除了這種方式可以實現排序以外,還有一種方式可以實現排序。

TreeSet有這么一個構造方法:

TreeSet(Comparator<? super E> comparator)      構造一個新的,空的樹集,根據指定的比較器進行排序。

Comparator是什么呢?API文檔看一下:

Interface Comparator<T>,是一個接口,里面有一個要實現的接口方法:

int    compare(T o1, T o2)   比較其兩個參數的順序。

 例如,我們要對字符串的長度進行排序,長度相同的安裝字符串排序:

public class Test5 {
    public static void main(String[] args) {
        TreeSet<String> ts = new TreeSet<>(new SortedByLen());    // 父類引用指向子類對象 Comparator c = new SortedByLen();
        ts.add("aaaaaaaaa");
        ts.add("wc");
        ts.add("nba");
        ts.add("cba");
        ts.add("chichung");
        System.out.println(ts);
    }
}

class SortedByLen implements Comparator<String> {

    @Override
    public int compare(String o1, String o2) {
        int num = o1.length() - o2.length();
        return num == 0 ? o1.compareTo(o2) : num;
    }
}

//輸出結果:
[wc, cba, nba, chichung, aaaaaaaaa]

需要注意的是重寫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。

 


免責聲明!

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



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