TreeSet的理解和用法


treeset 底層用treemap實現,實現了sortedset接口。紅黑樹實現,不允許重復。可以自然和定制排序。
用到的例子:
1,ijkplayer中treeset存入mp4,flv。判斷后綴。
總結:
1、不能有重復的元素; 
2、具有排序功能; 
3、TreeSet中的元素必須實現Comparable接口並重寫compareTo()方法,TreeSet判斷元素是否重復 、以及確定元素的順序 靠的都是這個方法; 
①對於java類庫中定義的類,TreeSet可以直接對其進行存儲,如String,Integer等,因為這些類已經實現了Comparable接口); 
②對於自定義類,如果不做適當的處理,TreeSet中只能存儲一個該類型的對象實例,否則無法判斷是否重復。 
4、依賴TreeMap。 
5、相對HashSet,TreeSet的優勢是有序,劣勢是相對讀取慢。根據不同的場景選擇不同的集合。
 
Set<String> treeset = new TreeSet<String>();
treeset.add("treeset1");
treeset.add("treeset2");
treeset.add("treeset3");
treeset.add("treeset4");
treeset.add("treeset10");
treeset.add("treeset9");
String stree = "treeset2";
treeset.add(stree);
treeset.add("tree9");
 
// 測試,執行添加了8次,但是treeset.add(stree);  重復,上述元素打印出來有  7 個。
 
System.out.println(treeset.size());
Iterator<String> treesetIt = treeset.iterator();
while(treesetIt.hasNext()){
    //  System.out.println(listIt.next());
    System.out.println(treesetIt.next());
}
打印結果:(按照字符串規則,字典查找法上升排序)
  7 (元素個數)
    tree9
    treeset1
    treeset10
    treeset2
    treeset3
    treeset4
    treeset9
 
如果treeset存的是整形,那么排列是:
Set<Integer> treeset1 = new TreeSet<>();
treeset1.add(1);
treeset1.add(2);
treeset1.add(3);
treeset1.add(4);
treeset1.add(10);
treeset1.add(9);
int stree22 = 22;
treeset1.add(stree22);
treeset1.add(9);
打印:
   7(元素)
    1    2   3    4    9   10    22
 
 
如果存的是一個bean對象,例如Student
Set<Student> studentSet = new TreeSet<>();
studentSet.add(new Student("張三1",18,99));
studentSet.add(new Student("張三2",17,100));
studentSet.add(new Student("張三3",22,60));
studentSet.add(new Student("張三4",43,69));
studentSet.add(new Student("張三4",20,92));
 
System.out.println(studentSet.size());
//使用迭代器遍歷Set集合
Iterator<Student> setIt = studentSet.iterator();
while(setIt.hasNext()){
    System.out.println(setIt.next());
}
 
結果:拋出異常
     Caused by: java.lang.ClassCastException: com.leesoo.lee.myplayer.ui.Student cannot be cast to java.lang.Comparable
違背了第三點:student類沒有 實現Comparable接口,不能存進treeset,無法判斷重復。
 
如何解決:
如何指定比較的規則,需要在自定義類(Student)中實現 Comparable接口,並重寫接口中的compareTo方法
public class Student implements Comparable<Student>{   //這個《student》要加,跟下面比較對應     private String name;     private int age;     ... @Override
public int compareTo(@NonNull Student 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,元素值每次比較,都認為新插入的元素比上一個元素小,於是二叉樹存儲時,會存在根的左側,讀取時就是倒序序排列的。
 
 
最終:student類添加到treeset時候,比較方法是   1 ,那么
插進去時候是   123456   打印出來也是123456
如果 compareTo方法返回負數,插進去時候是   123456   打印出來就是654321
 
 
對java類庫的String 字符串重新排序,不按照字典了,按照長度來排列。
 
只需要在新建treeset 時候,構造函數傳入自定義的排序規則即可。
 
//定義一個類,實現Comparator接口,並重寫compare()方法, class CompareByLength implements Comparator<String> {
    @Override     public int compare(String s1, String s2) {        //按照字符串的長度比較         int num = s1.length() - s2.length();        //長度為主要條件         return num == 0 ? s1.compareTo(s2) : num;    //內容為次要條件     } }
 
 
 
原來:
增加自定義排序后:
I/System.out: [t9, tree9, treeset2, treeset3, treeset4, treeset9, treeset10, treeset21, treeset1111]
 
 
比較方法對比:
 
第一種是添加自定義對象元素時候,自定義類要實現 comparable<E>  接口,添加后,要重寫
compareTo() 方法,這個方法傳進一個對象類進來。
implements Comparable<Student>
public int compareTo(@NonNull Student o) {
第二種是添加一個排序規則類,實現 Comparator<E>接口,並重寫compare()方法,這個方法傳進2個字符串,來比較。
implements Comparator<String>
public int compare(String s1, String s2)    
 
 
想法1: Student元素在treeset中 按照分數排列。
student實現 Comparable<Student> 接口,重新 compareTo 方法。
返回這個即可:
public int compareTo(@NonNull Student student) {
   return this.score -student.score;
  //  return 0;
}
 
上面不能這么寫,表面是看起來是按照student的分數進行比較存放,大於當前放右邊,小於放左邊。
實際上上面有個缺陷。如果2個學生 分數一樣。 那么treeset 就會認為這個元素對象一樣,就不會存進集合里面!!!!
java集合的工具類Collections中提供了兩種排序的方法,分別是:
  1. Collections.sort(List list)
  2. Collections.sort(List list,Comparator c)
第一種稱為自然排序,參與排序的對象需實現comparable接口,重寫其compareTo()方法,方法體中實現對象的比較大小規則
 
注意注意!!!Collections  只能對LIST 進行排序,並不能對set排序。 看它傳入的對象參數是list。
 
解決1:
改用ArrayList 來存儲.
private static ArrayList<Student> studentSet = new ArrayList<>();
studentSet.add(new Student("張三1", 18, 99));
studentSet.add(new Student("張三2", 17, 100));
studentSet.add(new Student("張三3", 22, 60));
studentSet.add(new Student("張三4", 43, 69));
studentSet.add(new Student("張三4", 20, 92));
studentSet.add(new Student("張三5", 17, 92));
 
// 這個類對集合ArrayList進行排序
Collections.sort(studentSet);
// 打印遍歷集合
Iterator<Student> setIt1 = studentSet.iterator();
while (setIt1.hasNext()) {
    System.out.println(setIt1.next());
}
打印結果: 自然排序按分數升序排序后:  (因為student類實現了分數比較的方法)
Student{name='張三3', age=22, score=60}
Student{name='張三4', age=43, score=69}
Student{name='張三4', age=20, score=92}
Student{name='張三5', age=17, score=92}
Student{name='張三1', age=18, score=99}
Student{name='張三2', age=17, score=100}
 
改用自定義排序: 需要實現 Comparator<E>接口,這個可以匿名實現。
自定義排序比第一種好,解耦強,如果更改排序規則只需要傳進不同的比較器即可。不用修改類的比較接口。
Collections.sort(studentSet);// 不傳參數這樣表示默認排序,用student類的自然排序
Collections.sort(studentSet, new Comparator<Student>()  //傳參數這樣表示定制排序,用這個定制的規則來排序。
 
定制排序:按年齡排序:
     Collections.sort(studentSet, new Comparator<Student>() {
          @Override
          public int compare(Student o1, Student o2) {
//              if (o1 instanceof Student && o2 instanceof Student) {
//                  Student e1 = (Student) o1;
//                  Student e2 = (Student) o2;
//                  return e1.getAge() - e2.getAge();
//              }
//              throw new ClassCastException("不能轉換為Student類型");
              return o2.getAge() - o1.getAge();
          }
      });
傳進一個比較器,獲取2個student的對象,然后獲取這個對象的age屬性,對這個屬性進行排序。
最終打印:
Student{name='張三4', age=43, score=69}
Student{name='張三3', age=22, score=60}
Student{name='張三4', age=20, score=92}
Student{name='張三1', age=18, score=99}
Student{name='張三2', age=17, score=100}
Student{name='張三5', age=17, score=92}
 
if (o2.getAge() == o1.getAge()) {
    return o1.getScore() - o2.getScore();
} else
    return o2.getAge() - o1.getAge();
 
打印: 最后2個92,100的分數就換過來了
Student{name='張三4', age=43, score=69}
Student{name='張三3', age=22, score=60}
Student{name='張三4', age=20, score=92}
Student{name='張三1', age=18, score=99}
Student{name='張三5', age=17, score=92}
Student{name='張三2', age=17, score=100}
 
關於自定義類實現compare接口,放進treeset。
 
treeset怎么判定2個自定義的類 是否相等?
自定義類要實現2個接口,一個是equal,一個是hashcode 。通過這2個判定2個對象是否重復。
 
1 HashSet的作用就是去除重復的對象,而TreeSet的主要作用就是排序compareTo(obj) 方法
2 HashSet是由哈希算法來實現的,集合存儲時先判斷其hashCode()值一樣嗎,不一樣直接存  若一樣再調用equals方法進行比較   所以不僅要重寫hashCode方法 還要重寫 equals方法
 
所以要排序就要用treeset,自定義類實現compare to 方法。
要去重復要用到hashset,自定義類要實現hashcode和equals方法。
 
1.HashSet的a.基本數據類型和b.引用類型數據
[Student [name=張三, age=18], Student [name=張三, age=18], Student [name=李四, age=19]
 
可以看出,倆張三都被添加進去了,但是重復的我們並不想添加進去,為什么會被添加進去呢
這時因為在創建student的時候,我們給每個對象都在堆內存中開辟了一片空間,雖然倆對象中的元素相等,但是他們的地址是不一樣的,所以系統認為他們是不同的,所以被添加進去了
如果我們要讓系統把student中重復的去掉,我們只能從底層去操作,因為HashSet是系統內部根據比較Hash值來判斷的,然后在調用equals方法來比較的,所以我們要重寫Object類中的hashCode和equals方法
因為我們要對Student類的對象排序,自然是Student類的對象來調用,所以在Student類中進行方法的重寫
 
 
 
關於自定義類student,可以重寫它的hashcode和equals。 compareto方法最好不寫,用比較器來寫。因為compareto 一般裝在treeset集合。 如果student要裝在hashset  和treeset。 外部寫比較器傳入比較好。 這樣即使student類被裝到很多個集合中,並且排序的條件不同,我們也可以為每一個集合創建一個特有的比較器來比較
 
 
 
 
 

 


免責聲明!

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



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