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()方法去和上一個插入的元素作比較,並按二叉樹的結構進行排列。
- 如果將compareTo()返回值寫死為0,元素值每次比較,都認為是相同的元素,這時就不再向TreeSet中插入除第一個外的新元素。所以TreeSet中就只存在插入的第一個元素。
- 如果將compareTo()返回值寫死為1,元素值每次比較,都認為新插入的元素比上一個元素大,於是二叉樹存儲時,會存在根的右側,讀取時就是正序排列的。
- 如果將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; //內容為次要條件 } }
@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中提供了兩種排序的方法,分別是:
- Collections.sort(List list)
- 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類被裝到很多個集合中,並且排序的條件不同,我們也可以為每一個集合創建一個特有的比較器來比較