1、TreeSet類
1)TreeSet類概述
使用元素的自然順序對元素進行排序
或者根據創建 set 時提供的 Comparator 進行排序
具體取決於使用的
構造方法。
2)TreeSet是如何保證元素的排序和唯一性的
底層數據結構是紅黑樹(紅黑樹是一種自平衡的二叉樹)
例子1:
package treesetdemos; import java.util.TreeSet; /** * Created by gao on 15-12-17. */ /* * TreeSet:能夠對元素按照某種規則進行排序。 * 排序有兩種方式 * A:自然排序 * B:比較器排序 * * TreeSet集合的特點:排序和唯一 * * 通過觀察TreeSet的add()方法,我們知道最終要看TreeMap的put()方法。 */ public class TreeDemo01 { public static void main(String[] args) { // 創建集合對象 // 自然順序進行排序 TreeSet<Integer> ts = new TreeSet<Integer>(); // 創建元素並添加 // 20,18,23,22,17,24,19,18,24 ts.add(20); ts.add(18); ts.add(23); ts.add(22); ts.add(17); ts.add(24); ts.add(19); ts.add(18); ts.add(24); // 遍歷 for(Integer i : ts){ System.out.println(i); } } }
輸出結果:
17
18
19
20
22
23
24
3)TreeSet的add()方法的源碼解析
interface Collection {...} interface Set extends Collection {...} interface NavigableMap { } class TreeMap implements NavigableMap { public V put(K key, V value) { Entry<K,V> t = root; if (t == null) { compare(key, key); // type (and possibly null) check root = new Entry<>(key, value, null); size = 1; modCount++; return null; } int cmp; Entry<K,V> parent; // split comparator and comparable paths Comparator<? super K> cpr = comparator; if (cpr != null) { do { parent = t; cmp = cpr.compare(key, t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } else { if (key == null) throw new NullPointerException(); Comparable<? super K> k = (Comparable<? super K>) key; do { parent = t; cmp = k.compareTo(t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } Entry<K,V> e = new Entry<>(key, value, parent); if (cmp < 0) parent.left = e; else parent.right = e; fixAfterInsertion(e); size++; modCount++; return null; } } class TreeSet implements Set { private transient NavigableMap<E,Object> m; public TreeSet() { this(new TreeMap<E,Object>()); } public boolean add(E e) { return m.put(e, PRESENT)==null; } } 真正的比較是依賴於元素的compareTo()方法,而這個方法是定義在 Comparable里面的。 所以,你要想重寫該方法,就必須是先實現 Comparable接口。這個接口表示的就是自然排序。
4)TreeSet存儲元素自然排序和唯一的圖解

例子2:存儲自定義對象並保證排序唯一
自定義類:先按年齡排序,年齡相同按姓名自然排序
package treesetdemos; /** * @author Administrator * */ /* * 如果一個類的元素要想能夠進行自然排序,就必須實現自然排序接口 */ 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; } @Override public int compareTo(Student s) { // return 0; // return 1; // return -1; // 這里返回什么,其實應該根據我的排序規則來做 // 按照年齡排序,主要條件 int num = this.age - s.age; // 次要條件 // 年齡相同的時候,還得去看姓名是否也相同 // 如果年齡和姓名都相同,才是同一個元素 int num2 = num == 0 ? this.name.compareTo(s.name) : num; return num2; } }
測試類:(TreeSet無參構造,自然排序)
package treesetdemos; import java.util.TreeSet; /** * Created by gao on 15-12-17. */ /* * TreeSet存儲自定義對象並保證排序和唯一。 * * A:你沒有告訴我們怎么排序 * 自然排序,按照年齡從小到大排序 * B:元素什么情況算唯一你也沒告訴我 * 成員變量值都相同即為同一個元素 */ public class TreeSetDemo02 { public static void main(String[] args) { // 創建集合對象 TreeSet<Student> ts = new TreeSet<Student>(); // 創建元素 // Student s1 = new Student("linqingxia", 27); Student s2 = new Student("zhangguorong", 29); Student s3 = new Student("wanglihong", 23); Student s4 = new Student("linqingxia", 27); Student s5 = new Student("liushishi", 22); Student s6 = new Student("wuqilong", 40); Student s7 = new Student("fengqingy", 22); Student s8 = new Student("linqingxia", 29); // 添加元素 ts.add(s1); ts.add(s2); ts.add(s3); ts.add(s4); ts.add(s5); ts.add(s6); ts.add(s7); ts.add(s8); // 遍歷 for (Student s : ts) { System.out.println(s.getName() + "---" + s.getAge()); } } }
輸出結果:
fengqingy---22
liushishi---22
wanglihong---23
linqingxia---27
linqingxia---29
zhangguorong---29
wuqilong---40
例子3:按照姓名的長度排序
自定義學生類:
package treesetdemos; /** * Created by gao on 15-12-17. */ public class Student02 implements Comparable<Student02> { private String name; private int age; public Student02() { super(); } public Student02(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; } @Override public int compareTo(Student02 o) { // 主要條件 姓名的長度 int num = this.name.length() - o.name.length(); // 姓名的長度相同,不代表姓名的內容相同 int num2 = num == 0 ? this.name.compareTo(o.name) : num; // 姓名的長度和內容相同,不代表年齡相同,所以還得繼續判斷年齡 int num3 = num2 == 0 ? this.age - o.age : num2; return num3; } }
測試類:(TreeSet無參構造,自然排序)
package treesetdemos; import java.util.TreeSet; /** * Created by gao on 15-12-17. */ /* * 需求:請按照姓名的長度排序 */ public class TreeSetDemo03 { public static void main(String[] args) { // 創建集合對象 TreeSet<Student02> ts = new TreeSet<Student02>(); // 創建元素 // Student02 s1 = new Student02("linqingxia", 27); Student02 s2 = new Student02("zhangguorong", 29); Student02 s3 = new Student02("wanglihong", 23); Student02 s4 = new Student02("linqingxia", 27); Student02 s5 = new Student02("liushishi", 22); Student02 s6 = new Student02("wuqilong", 40); Student02 s7 = new Student02("fengqingy", 22); Student02 s8 = new Student02("linqingxia", 29); // 添加元素 ts.add(s1); ts.add(s2); ts.add(s3); ts.add(s4); ts.add(s5); ts.add(s6); ts.add(s7); ts.add(s8); // 遍歷 for (Student02 s : ts) { System.out.println(s.getName() + "---" + s.getAge()); } } }
輸出結果:
fengqingy---22
liushishi---22
wanglihong---23
linqingxia---27
linqingxia---29
zhangguorong---29
wuqilong---40
5)自定義比較器
學生類:
package comparabledemos; /** * @author Administrator * */ 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; } }
方式一:新建一個接口類MyComparator 實現Comparator接口。
MyComparator接口類:
package comparabledemos; import java.util.Comparator; /** * Created by gao on 15-12-18. */ public class MyComparator implements Comparator<Student> { @Override public int compare(Student s1, Student s2) { // int num = this.name.length() - s.name.length(); // this -- s1 // s -- 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; } }
測試類:(TreeSet有參構造,調用比較器排序)
package comparabledemos; import java.util.TreeSet; /** * Created by gao on 15-12-18. */ /* * 需求:請按照姓名的長度排序 * * TreeSet集合保證元素排序和唯一性的原理 * 唯一性:是根據比較的返回是否是0來決定。 * 排序: * A:自然排序(元素具備比較性) * 讓元素所屬的類實現自然排序接口 Comparable * B:比較器排序(集合具備比較性) * 讓集合的構造方法接收一個比較器接口的子類對象 Comparator */ public class MyComparableDemo { public static void main(String[] args) { // 創建集合對象 // TreeSet<Student> ts = new TreeSet<Student>(); //自然排序 // public TreeSet(Comparator comparator) //比較器排序 TreeSet<Student> ts = new TreeSet<Student>(new MyComparator()); // 創建元素 Student s1 = new Student("linqingxia", 27); Student s2 = new Student("zhangguorong", 29); Student s3 = new Student("wanglihong", 23); Student s4 = new Student("linqingxia", 27); Student s5 = new Student("liushishi", 22); Student s6 = new Student("wuqilong", 40); Student s7 = new Student("fengqingy", 22); Student s8 = new Student("linqingxia", 29); // 添加元素 ts.add(s1); ts.add(s2); ts.add(s3); ts.add(s4); ts.add(s5); ts.add(s6); ts.add(s7); ts.add(s8); // 遍歷 for (Student s : ts) { System.out.println(s.getName() + "---" + s.getAge()); } } }
方式二:直接創建匿名內部類實現比較器
package comparabledemos; import java.util.Comparator; import java.util.TreeSet; /** * Created by gao on 15-12-18. */ /* * 需求:請按照姓名的長度排序 * * TreeSet集合保證元素排序和唯一性的原理 * 唯一性:是根據比較的返回是否是0來決定。 * 排序: * A:自然排序(元素具備比較性) * 讓元素所屬的類實現自然排序接口 Comparable * B:比較器排序(集合具備比較性) * 讓集合的構造方法接收一個比較器接口的子類對象 Comparator */ public class MyComparableDemo { public static void main(String[] args) { // 創建集合對象 // TreeSet<Student> ts = new TreeSet<Student>(); //自然排序 // public TreeSet(Comparator comparator) //比較器排序 //TreeSet<Student> ts = new TreeSet<Student>(new MyComparator()); // 如果一個方法的參數是接口,那么真正要的是接口的實現類的對象 // 而匿名內部類就可以實現這個東西 TreeSet<Student> ts = 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("linqingxia", 27); Student s2 = new Student("zhangguorong", 29); Student s3 = new Student("wanglihong", 23); Student s4 = new Student("linqingxia", 27); Student s5 = new Student("liushishi", 22); Student s6 = new Student("wuqilong", 40); Student s7 = new Student("fengqingy", 22); Student s8 = new Student("linqingxia", 29); // 添加元素 ts.add(s1); ts.add(s2); ts.add(s3); ts.add(s4); ts.add(s5); ts.add(s6); ts.add(s7); ts.add(s8); // 遍歷 for (Student s : ts) { System.out.println(s.getName() + "---" + s.getAge()); } } }
輸出結果:
wuqilong---40
fengqingy---22
liushishi---22
linqingxia---27
linqingxia---29
wanglihong---23
zhangguorong---29
例子4:編寫一個程序,獲取10個1至20的隨機數,要求隨機數不能重復。
package comparabledemos; import java.util.HashSet; import java.util.Random; /** * Created by gao on 15-12-18. */ /* * 編寫一個程序,獲取10個1至20的隨機數,要求隨機數不能重復。 * * 分析: * A:創建隨機數對象 * B:創建一個HashSet集合 * C:判斷集合的長度是不是小於10 * 是:就創建一個隨機數添加 * 否:不搭理它 * D:遍歷HashSet集合 */ public class HashSetDemo { public static void main(String[] args) { // 創建隨機數對象 Random r = new Random(); // 創建一個Set集合 HashSet<Integer> hs = new HashSet<Integer>(); // 判斷集合的長度是不是小於10 while (hs.size() < 10) { int x = r.nextInt(20) + 1; hs.add(x); } // 遍歷Set集合 for (int x : hs) { System.out.println(x); } } }
例子5:鍵盤錄入5個學生信息(姓名,語文成績,數學成績,英語成績),按照總分從高到低輸出到控制台
學生類:
package comparabledemos; /** * @author Administrator * */ public class Student { // 姓名 private String name; // 語文成績 private int chinese; // 數學成績 private int math; // 英語成績 private int english; public Student() { super(); } public Student(String name, int chinese, int math, int english) { this.name = name; this.chinese = chinese; this.math = math; this.english = english; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getChinese() { return chinese; } public void setChinese(int chinese) { this.chinese = chinese; } public int getMath() { return math; } public void setMath(int math) { this.math = math; } public int getEnglish() { return english; } public void setEnglish(int english) { this.english = english; } public int getSum(){ return this.chinese + this.english + this.math; } }
測試類:
package comparabledemos; import java.util.Comparator; import java.util.Scanner; import java.util.TreeSet; /** * Created by gao on 15-12-18. */ /* * 鍵盤錄入5個學生信息(姓名,語文成績,數學成績,英語成績),按照總分從高到低輸出到控制台 * * 分析: * A:定義學生類 * B:創建一個TreeSet集合 * C:總分從高到底如何實現呢? * D:鍵盤錄入5個學生信息 * E:遍歷TreeSet集合 */ public class TreeSetDemo { public static void main(String[] args) { TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() { @Override // 創建一個TreeSet集合 public int compare(Student s1, Student s2) { // 總分從高到低(注意這里是s2減s1) int num = s2.getSum() - s1.getSum(); // 總分相同的不一定語文相同 int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num; // 總分相同的不一定數序相同 int num3 = num2 == 0 ? s1.getMath() - s2.getMath() : num2; // 總分相同的不一定英語相同 int num4 = num3 == 0 ? s1.getEnglish() - s2.getEnglish() : num3; // 姓名還不一定相同 int num5 = num4 == 0 ? s1.getName().compareTo(s2.getName()) : num4; return num5; } }); System.out.println("學生信息錄入開始"); // 鍵盤錄入5個學生信息 for(int x = 1; x <= 5; x++){ Scanner sc = new Scanner(System.in); System.out.println("請輸入第" + x + "個學生的姓名:"); String name = sc.nextLine(); System.out.println("請輸入第" + x + "個學生的語文成績:"); String chineseString = sc.nextLine(); System.out.println("請輸入第" + x + "個學生的數學成績:"); String mathString = sc.nextLine(); System.out.println("請輸入第" + x + "個學生的英語成績:"); String englishString = sc.nextLine(); // 把數據封裝到學生對象中 Student s = new Student(); s.setName(name); s.setChinese(Integer.parseInt(chineseString)); s.setMath(Integer.parseInt(mathString)); s.setEnglish(Integer.parseInt(englishString)); // 把學生對象添加到集合 ts.add(s); } System.out.println("學生信息錄入完畢"); System.out.println("學習信息從高到低排序如下:"); System.out.println("姓名\t語文成績\t數學成績\t英語成績"); // 遍歷集合 for (Student s : ts) { System.out.println(s.getName() + "\t" + s.getChinese() + "\t" + s.getMath() + "\t" + s.getEnglish()); } } }
輸出結果:
學生信息錄入開始
請輸入第1個學生的姓名:
小明
請輸入第1個學生的語文成績:
89
請輸入第1個學生的數學成績:
90
請輸入第1個學生的英語成績:
91
請輸入第2個學生的姓名:
小紅
請輸入第2個學生的語文成績:
99
請輸入第2個學生的數學成績:
95
請輸入第2個學生的英語成績:
100
請輸入第3個學生的姓名:
小青
請輸入第3個學生的語文成績:
95
請輸入第3個學生的數學成績:
96
請輸入第3個學生的英語成績:
99
請輸入第4個學生的姓名:
小高
請輸入第4個學生的語文成績:
99
請輸入第4個學生的數學成績:
100
請輸入第4個學生的英語成績:
100
請輸入第5個學生的姓名:
小楊
請輸入第5個學生的語文成績:
85
請輸入第5個學生的數學成績:
80
請輸入第5個學生的英語成績:
60
學生信息錄入完畢
學習信息從高到低排序如下:
姓名
語文成績 數學成績 英語成績
小高
99 100 100
小紅
99 95 100
小青
95 96 99
小明
89 90 91
小楊
85 80 60