Java API —— TreeSet类
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存储元素自然排序和唯一的图解
![](https://images2015.cnblogs.com/blog/826812/201512/826812-20151218205419084-62858342.png)
例子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