JavaSE基础 TreeSet集合(红黑树数据结构)

Posted 稻香的超人不会飞

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaSE基础 TreeSet集合(红黑树数据结构)相关的知识,希望对你有一定的参考价值。

本文主要介绍TreeSet集合的基本使用方法

TreeSet之自然排序

自然排序(元素具备比较性):让元素所属的类实现自然排序接口 Comparable

先看一个简单的例子,TreeSet集合,存入整数,进行排序

 1import java.util.TreeSet;
2
3/*
4 * TreeSet:能够对元素按照某种规则进行排序。
5 * 排序有两种方式
6 * A:自然排序
7 * B:比较器排序
8 *
9 * TreeSet集合的特点:排序和唯一
10 *
11 * 通过观察TreeSet的add()方法,我们知道最终要看TreeMap的put()方法。
12 */

13public class TreeSetDemo {
14    public static void main(String[] args) {
15        // 创建集合对象
16        // 自然顺序进行排序
17        TreeSet<Integer> ts = new TreeSet<Integer>();
18
19        // 创建元素并添加
20        // 20,18,23,22,17,24,19,18,24
21        ts.add(20);
22        ts.add(18);
23        ts.add(23);
24        ts.add(22);
25        ts.add(17);
26        ts.add(24);
27        ts.add(19);
28        ts.add(18);
29        ts.add(24);
30
31        // 遍历
32        for (Integer i : ts) {
33            System.out.println(i);
34        }
35    }
36}

输出结果:

  • 此处能够对整数进行排序,是因为Integer类实现了Comparable接口.  

  • Comparable接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法。

再来看一个例子,在TreeSet中,存入学生对象的数据,并根据学生的年龄从小到大进行排序

  • 创建一个学生类,实现自然排序Comparable接口,传入的泛型为Student

 1/*
2 * 如果一个类的元素要想能够进行自然排序,就必须实现自然排序接口
3 */

4public class Student implements Comparable<Student> {
5    private String name;
6    private int age;
7
8    public Student() {
9        super();
10    }
11
12    public Student(String name, int age) {
13        super();
14        this.name = name;
15        this.age = age;
16    }
17
18    public String getName() {
19        return name;
20    }
21
22    public void setName(String name) {
23        this.name = name;
24    }
25
26    public int getAge() {
27        return age;
28    }
29
30    public void setAge(int age) {
31        this.age = age;
32    }
33
34    @Override
35    public int compareTo(Student s) {
36        // return 0;
37        // return 1;
38        // return -1;
39
40        // 这里返回什么,其实应该根据我的排序规则来做
41        // 按照年龄排序,主要条件
42        int num = this.age - s.age;
43        // 次要条件
44        // 年龄相同的时候,还得去看姓名是否也相同
45        // 如果年龄和姓名都相同,才是同一个元素
46        int num2 = num == 0 ? this.name.compareTo(s.name) : num;
47        return num2;
48    }
49}
  • 编写测试类

 1import java.util.TreeSet;
2
3/*
4 * TreeSet存储自定义对象并保证排序和唯一。
5 *
6 * A:你没有告诉我们怎么排序
7 *         自然排序,按照年龄从小到大排序
8 * B:元素什么情况算唯一你也没告诉我
9 *         成员变量值都相同即为同一个元素
10 */

11public class TreeSetDemo2 {
12    public static void main(String[] args) {
13        // 创建集合对象
14        TreeSet<Student> ts = new TreeSet<Student>();
15
16        // 创建元素
17        Student s1 = new Student("linqingxia", 27);
18        Student s2 = new Student("zhangguorong", 29);
19        Student s3 = new Student("wanglihong", 23);
20        Student s4 = new Student("linqingxia", 27);
21        Student s5 = new Student("liushishi", 22);
22        Student s6 = new Student("wuqilong", 40);
23        Student s7 = new Student("fengqingy", 22);
24
25        // 添加元素
26        ts.add(s1);
27        ts.add(s2);
28        ts.add(s3);
29        ts.add(s4);
30        ts.add(s5);
31        ts.add(s6);
32        ts.add(s7);
33
34        // 遍历
35        for (Student s : ts) {
36            System.out.println(s.getName() + "---" + s.getAge());
37        }
38    }
39}

运行结果:

JavaSE基础 TreeSet集合(红黑树数据结构)
  • 可以看到,成功将学生的信息按照年龄从小到大输出了,并把重复的学生信息去除了

再更改需求添加学生数据,并根据学生的姓名长度,从小到大排序

  • 创建一个学生类,实现自然排序Comparable接口,传入的泛型为Student,重写compareTo方法

 1public class Student implements Comparable<Student> {
2    private String name;
3    private int age;
4
5    public Student() {
6        super();
7    }
8
9    public Student(String name, int age) {
10        super();
11        this.name = name;
12        this.age = age;
13    }
14
15    public String getName() {
16        return name;
17    }
18
19    public void setName(String name) {
20        this.name = name;
21    }
22
23    public int getAge() {
24        return age;
25    }
26
27    public void setAge(int age) {
28        this.age = age;
29    }
30
31    @Override
32    public int compareTo(Student s) {
33        // 主要条件 姓名的长度
34        int num = this.name.length() - s.name.length();
35        // 姓名的长度相同,不代表姓名的内容相同
36        int num2 = num == 0 ? this.name.compareTo(s.name) : num;
37        // 姓名的长度和内容相同,不代表年龄相同,所以还得继续判断年龄
38        int num3 = num2 == 0 ? this.age - s.age : num2;
39        return num3;
40    }
41}
  • 编写测试类

 1/*
2 * 需求:请按照姓名的长度排序
3 */

4public class TreeSetDemo {
5    public static void main(String[] args) {
6        // 创建集合对象
7        TreeSet<Student> ts = new TreeSet<Student>();
8
9        // 创建元素
10        Student s1 = new Student("linqingxia", 27);
11        Student s2 = new Student("zhangguorong", 29);
12        Student s3 = new Student("wanglihong", 23);
13        Student s4 = new Student("linqingxia", 27);
14        Student s5 = new Student("liushishi", 22);
15        Student s6 = new Student("wuqilong", 40);
16        Student s7 = new Student("fengqingy", 22);
17        Student s8 = new Student("linqingxia", 29);
18
19        // 添加元素
20        ts.add(s1);
21        ts.add(s2);
22        ts.add(s3);
23        ts.add(s4);
24        ts.add(s5);
25        ts.add(s6);
26        ts.add(s7);
27        ts.add(s8);
28
29        // 遍历
30        for (Student s : ts) {
31            System.out.println(s.getName() + "---" + s.getAge());
32        }
33    }
34}

运行结果:

JavaSE基础 TreeSet集合(红黑树数据结构)
  • 可以看到,成功按照姓名的长度排序,由长到短输出了.

TreeSet之比较器排序

比较器排序(集合具备比较性):让集合的构造方法接收一个比较器接口的子类对象 Comparator

还是上一步的要求:添加学生数据,并根据学生的姓名长度,从小到大排序.改为比较器排序做.

  • 创建学生类

 1public class Student {
2    private String name;
3    private int age;
4
5    public Student() {
6        super();
7    }
8
9    public Student(String name, int age) {
10        super();
11        this.name = name;
12        this.age = age;
13    }
14
15    public String getName() {
16        return name;
17    }
18
19    public void setName(String name) {
20        this.name = name;
21    }
22
23    public int getAge() {
24        return age;
25    }
26
27    public void setAge(int age) {
28        this.age = age;
29    }
30}
  • 创建一个比较器接口的子类对象

 1import java.util.Comparator;
2
3public class MyComparator implements Comparator<Student> {
4
5    @Override
6    public int compare(Student s1, Student s2) {
7        // int num = this.name.length() - s.name.length();
8        // this -- s1
9        // s -- s2
10        // 姓名长度
11        int num = s1.getName().length() - s2.getName().length();
12        // 姓名内容
13        int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
14        // 年龄
15        int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
16        return num3;
17    }
18
19}
  • 编写测试类

 1import java.util.TreeSet;
2
3/*
4 * 需求:请按照姓名的长度排序
5 *
6 * TreeSet集合保证元素排序和唯一性的原理
7 * 唯一性:是根据比较的返回是否是0来决定。
8 * 排序:
9 *         A:自然排序(元素具备比较性)
10 *             让元素所属的类实现自然排序接口 Comparable
11 *         B:比较器排序(集合具备比较性)
12 *             让集合的构造方法接收一个比较器接口的子类对象 Comparator
13 */

14public class TreeSetDemo {
15    public static void main(String[] args) {
16        // 创建集合对象
17        //TreeSet<Student> ts = new TreeSet<Student>(); //自然排序
18        // public TreeSet(Comparator comparator) //比较器排序
19        TreeSet<Student> ts = new TreeSet<Student>(new MyComparator());
20
21
22
23        // 创建元素
24        Student s1 = new Student("linqingxia", 27);
25        Student s2 = new Student("zhangguorong", 29);
26        Student s3 = new Student("wanglihong", 23);
27        Student s4 = new Student("linqingxia", 27);
28        Student s5 = new Student("liushishi", 22);
29        Student s6 = new Student("wuqilong", 40);
30        Student s7 = new Student("fengqingy", 22);
31        Student s8 = new Student("linqingxia", 29);
32
33        // 添加元素
34        ts.add(s1);
35        ts.add(s2);
36        ts.add(s3);
37        ts.add(s4);
38        ts.add(s5);
39        ts.add(s6);
40        ts.add(s7);
41        ts.add(s8);
42
43        // 遍历
44        for (Student s : ts) {
45            System.out.println(s.getName() + "---" + s.getAge());
46        }
47    }
48}
  • 运行结果:

    JavaSE基础 TreeSet集合(红黑树数据结构)
  • 成功按照名字长度排序输出

上题中,匿名内部类的写法:如果一个方法的参数是接口,那么真正要的是接口的实现类的对象,而匿名内部类就可以实现这个东西

 1public class TreeSetDemo {
2    public static void main(String[] args) {
3
4        // 如果一个方法的参数是接口,那么真正要的是接口的实现类的对象
5        // 而匿名内部类就可以实现这个东西
6        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
7            @Override
8            public int compare(Student s1, Student s2) {
9                // 姓名长度
10                int num = s1.getName().length() - s2.getName().length();
11                // 姓名内容
12                int num2 = num == 0 ? s1.getName().compareTo(s2.getName())
13                        : num;
14                // 年龄
15                int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
16                return num3;
17            }
18        });
19
20        // 创建元素
21        Student s1 = new Student("linqingxia", 27);
22        Student s2 = new Student("zhangguorong", 29);
23        Student s3 = new Student("wanglihong", 23);
24        Student s4 = new Student("linqingxia", 27);
25        Student s5 = new Student("liushishi", 22);
26        Student s6 = new Student("wuqilong", 40);
27        Student s7 = new Student("fengqingy", 22);
28        Student s8 = new Student("linqingxia", 29);
29
30        // 添加元素
31        ts.add(s1);
32        ts.add(s2);
33        ts.add(s3);
34        ts.add(s4);
35        ts.add(s5);
36        ts.add(s6);
37        ts.add(s7);
38        ts.add(s8);
39
40        // 遍历
41        for (Student s : ts) {
42            System.out.println(s.getName() + "---" + s.getAge());
43        }
44    }
45}
  • 运行结果:

  • 可以看到,使用匿名内部类,可以同样按照姓名的长度输出,匿名内部类的写法看似复杂,但熟练之后,及其方便,不用新建一个类了.

TreeSet应用题

键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台
分析:
    A:定义学生类
    B:创建一个TreeSet集合
    C:总分从高到底如何实现呢?      
    D:键盘录入5个学生信息
    E:遍历TreeSet集合

  • 创建学生类

 1public class Student {
2    // 姓名
3    private String name;
4    // 语文成绩
5    private int chinese;
6    // 数学成绩
7    private int math;
8    // 英语成绩
9    private int english;
10
11    public Student(String name, int chinese, int math, int english) {
12        super();
13        this.name = name;
14        this.chinese = chinese;
15        this.math = math;
16        this.english = english;
17    }
18
19    public Student() {
20        super();
21    }
22
23    public String getName() {
24        return name;
25    }
26
27    public void setName(String name) {
28        this.name = name;
29    }
30
31    public int getChinese() {
32        return chinese;
33    }
34
35    public void setChinese(int chinese) {
36        this.chinese = chinese;
37    }
38
39    public int getMath() {
40        return math;
41    }
42
43    public void setMath(int math) {
44        this.math = math;
45    }
46
47    public int getEnglish() {
48        return english;
49    }
50
51    public void setEnglish(int english) {
52        this.english = english;
53    }
54
55    public int getSum() {
56        return this.chinese + this.math + this.english;
57    }
58}
  • 编写测试类,采用匿名内部类,实现Comparator的写法

 1import java.util.Comparator;
2import java.util.Scanner;
3import java.util.TreeSet;
4
5public class TreeSetDemo {
6    public static void main(String[] args) {
7        // 创建一个TreeSet集合
8        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
9            @Override
10            public int compare(Student s1, Student s2) {
11                // 总分从高到低
12                int num = s2.getSum() - s1.getSum();
13                // 总分相同的不一定语文相同
14                int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
15                // 总分相同的不一定数序相同
16                int num3 = num2 == 0 ? s1.getMath() - s2.getMath() : num2;
17                // 总分相同的不一定英语相同
18                int num4 = num3 == 0 ? s1.getEnglish() - s2.getEnglish() : num3;
19                // 姓名还不一定相同呢
20                int num5 = num4 == 0 ? s1.getName().compareTo(s2.getName())
21                        : num4;
22                return num5;
23            }
24        });
25
26        System.out.println("学生信息录入开始");
27        // 键盘录入5个学生信息
28        for (int x = 1; x <= 5; x++) {
29            Scanner sc = new Scanner(System.in);
30            System.out.println("请输入第" + x + "个学生的姓名:");
31            String name = sc.nextLine();
32            System.out.println("请输入第" + x + "个学生的语文成绩:");
33            String chineseString = sc.nextLine();
34            System.out.println("请输入第" + x + "个学生的数学成绩:");
35            String mathString = sc.nextLine();
36            System.out.println("请输入第" + x + "个学生的英语成绩:");
37            String englishString = sc.nextLine();
38
39            // 把数据封装到学生对象中
40            Student s = new Student();
41            s.setName(name);
42            s.setChinese(Integer.parseInt(chineseString));
43            s.setMath(Integer.parseInt(mathString));
44            s.setEnglish(Integer.parseInt(englishString));
45
46            // 把学生对象添加到集合
47            ts.add(s);
48        }
49        System.out.println("学生信息录入完毕");
50
51        System.out.println("学习信息从高到低排序如下:");
52        System.out.println("姓名 语文成绩 数学成绩 英语成绩");
53        // 遍历集合
54        for (Student s : ts) {
55            System.out.println(s.getName() + " " + s.getChinese() + " "
56                    + s.getMath() + " " + s.getEnglish());
57        }
58    }
59}
  • 运行结果

  • 可以看到成功按照学生总成绩,由大到小排序


以上是关于JavaSE基础 TreeSet集合(红黑树数据结构)的主要内容,如果未能解决你的问题,请参考以下文章

深入理解红黑树

JavaSE-集合

聊聊红黑树

java中treemap和treeset实现(红黑树)

集合之TreeSet(含JDK1.8源码分析)

通过分析 JDK 源代码研究 TreeMap 红黑树算法实