TreeSet集合的理解(自然排序和比較器排序)
TreeSet集合
TreeSet集合是Set集合的一個子實現類,它是基於TreeMap中的NavigableSet介面實現的
TreeSet集合是預設通過自然排序將集合中的元素進行排序
TreeSet有兩種排序方式:
1)自然排序
2)比較器排序
讓我們先來看看一個例題:
package com.TreeSetDome; import java.util.TreeSet; public class TreeSetDome { public static void main(String[] args) { TreeSet<Integer> set=new TreeSet<Integer>(); set.add(17); set.add(25); set.add(23); set.add(14); set.add(17); set.add(30); for(Integer s:set) { System.out.println(s); } } } 執行結果: 14 17 23 25 30
根據上述結果可以看出TreeSet集合是自然排序和去重的,為什麼會達到這樣的效果呢?
TreeSet集合的無參構造就是屬於自然排序
TreeSet<Integer> set=new TreeSet<Integer>();
這是因為TreeSet集合依賴於TreeMap的紅黑樹結構實現的,下面讓我們根據上述例題去看看紅黑樹結構的理解:
set.add(17);
set.add(25);
set.add(23);
set.add(14);
set.add(17);
set.add(30);
17先進行儲存,所以將17作為根節點,與後面的元素進行比較,25進來後與17相比,比17大,所以成為17的右孩子,放在
使用TreeSet進行自定義函式的排序,對年齡由小到大進行排序
package com.TreeSetDome; public class Student implements Comparable<Student>{ private String name; private int age; public Student() { super(); } public Student(String name, int age) { super(); this.name = name; this.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; } //因為上面Student類實現了comparable介面,所以必須重寫comparaTo方法才能達到排序的效果 @Override public int compareTo(Student s) { // return 0; /** * 因為這是我們自定義的類,系統並沒有告訴我們如何進行排序 * 所以需要我們自己手動進行排序 * 需求:按年齡由小到大進行排序 */ //年齡進行排序,由小到大 int num=this.age-s.age; //當年齡大小相等時,比較名字 int num2=num==0?this.name.compareTo(s.getName()):num; return num2; } }
package com.TreeSetDome;
import java.util.TreeSet;
public class StudentDome {
public static void main(String[] args) {
//建立TreeSet集合物件
TreeSet<Student> set=new TreeSet<Student>();
//建立學生物件,這裡的學生姓名不要寫成漢字,因為每個漢字的位元組大小不一樣
Student s1=new Student("dilireba",27);
Student s2=new Student("gaowen",25);
Student s3=new Student("zhaoxingxing",24);
Student s4=new Student("wuxuanyi",23);
Student s5=new Student("dilireba",27);
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
set.add(s5);
//增強for迴圈
for(Student st:set) {
System.out.println(st.getName()+"---"+st.getAge());
}
}
}
執行結果:
wuxuanyi---23
zhaoxingxing---24
gaowen---25
dilireba---27
由上例可以看出我們在學生類上實線了comparable介面,並且在學生類中重寫了comparaTo方法,當我們沒有進行以上的這些操作時,執行時就會出現這樣的錯誤,
Exception in thread "main" java.lang.ClassCastException: com.TreeSetDome.Student cannot be cast to java.lang.Comparable
因為沒有實現comparable介面,所以會出現以上的這個異常,但是為什麼在前面新增數字的時候並不需要實現comparable介面呢?這是新增數字時我們確定了型別為Integer型別,它本身就已經實現了comparable介面,不需要我們再去新增,具體可以去API中觀看,所以今後在使用TreeSet建立自定義類排序的時候,一定要自己去實現comparable介面,和重寫comparaTo方法
上述中我們重寫的ComparaTo方法是按照年齡來排序的,接下來讓我們按照姓名的長度以及年齡的大小來排序:
@Override
public int compareTo (Student s) {
/**
* 因為這是我們自定義的類,系統並沒有告訴我們如何進行排序
* 所以需要我們自己手動進行排序
* 需求:按姓名的長度來排序,然後再以年齡的大小來排序
*/
//按姓名的長短來排序,由小到大排序
int num=this.getName().length()-s.getName().length();
//再去比較的姓名的內容是否一致
int num2=num==0?this.getName().compareTo(s.getName()):num;
//名字一致,有時候並不是同一個人 還得再去比較年齡的大小
int num3=num2==0?this.age-s.age:num2;
return num3;
這是重寫的comparaTo方法,我新添加了一個學生變數Student s6=new Student("dilireba",25);
執行結果:
gaowen---25
dilireba---25
dilireba---27
wuxuanyi---23
zhaoxingxing---24
上面我們介紹了自然排序法,自然排序法主要就是運用TreeSet的無參構造,通過實現comparable介面中的comparaTo方法去進行自然排序,接下來讓我們看看比較器排序,看看二者的不同
package com.TreeSet;
public class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.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;
}
}
package com.TreeSet;
import java.util.Comparator;
import java.util.NavigableMap;
public class ComparatorDome implements Comparator<Student>{
@Override
/**
* 因為測試類中TreeSet集合的引用引數是介面,所以需要建立這個子實現類去實現這個介面
* 這裡的s1就相當於自然排序中的this,s2相當於s
* 先按名字的長度,當長度一致時,再按年齡的大小進行排序
*/
public int compare(Student s1, Student s2) {
//判斷姓名長度的大小
int num=s1.getName().length()-s2.getName().length();
//長度一致時,比較內容
int num2=num==0?s1.getName().compareTo(s2.getName()):num;
//內容一致時,比較年齡的大小
int num3=num2==0?(s1.getAge()-s2.getAge()):num2;
return num3;
}
}
package com.TreeSet;
import java.util.TreeSet;
public class StudentDome {
public static void main(String[] args) {
//建立TreeSet集合物件,運用比較器排序
//Comparator是一個介面,所以我們得建立一個子實現類去實現它
TreeSet<Student> set=new TreeSet<Student>(new ComparatorDome());
//建立學生物件
Student s1=new Student("dilireba",27);
Student s2=new Student("gaowen",25);
Student s3=new Student("zhaoxingxing",24);
Student s4=new Student("wuxuanyi",23);
Student s5=new Student("dilireba",25);
Student s6=new Student("dilireba",25);
//將學生物件新增到集合中
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
set.add(s5);
set.add(s6);
//增強for迴圈遍歷
for(Student s:set) {
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
執行結果:
gaowen---25
dilireba---25
dilireba---27
wuxuanyi---23
zhaoxingxing---24
除了建立子實現類去實現Comparator介面,我們還可以在測試類中通過匿名內部類的方式去測試,就不用單獨去建立一個子實現類了
在這裡學生類我就不寫了,上面有,直接寫測試類中的匿名內部類了
package com.TreeSet;
import java.util.Comparator;
import java.util.TreeSet;
public class StudentDome {
public static void main(String[] args) {
//建立TreeSet集合物件,運用比較器排序
//Comparator是一個介面,所以我們得建立一個子實現類去實現它
//匿名內部類的使用
TreeSet<Student> set=new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
//判斷姓名長度的大小
int num=s1.getName().length()-s2.getName().length();
//長度一致時,比較內容
int num2=num==0?s1.getName().compareTo(s2.getName()):num;
//內容一致時,比較年齡的大小
int num3=num2==0?(s1.getAge()-s2.getAge()):num2;
return num3;
}
});
//建立學生物件
Student s1=new Student("dilireba",27);
Student s2=new Student("gaowen",25);
Student s3=new Student("zhaoxingxing",24);
Student s4=new Student("wuxuanyi",23);
Student s5=new Student("dilireba",25);
Student s6=new Student("dilireba",25);
//將學生物件新增到集合中
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
set.add(s5);
set.add(s6);
//增強for迴圈遍歷
for(Student s:set) {
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
執行結果:
gaowen---25
dilireba---25
dilireba---27
wuxuanyi---23
zhaoxingxing---24