重点描述TreeSet的两种排序方式

目录

1.背景介绍

2.TreeSet保证元素唯一和排序的原理

3.两种排序(自然排序,比较器排序)

4.自然排序的实现

4.1 Integer和String对象都可以进行默认的TreeSet排序

4.2 对自定义对象进行 自然排序

5.比较器排序的实现

5.1 一般比较器写法

5.2 常见比较器写法


1.背景介绍

  1. TreeSet底层数据结构采用红黑树(平衡二叉树)来实现,元素唯一且已经排好序;唯一性同样需要重写hashCode和equals()方法,二叉树结构保证了元素的有序性。根据构造方法不同,分为自然排序(无参构造)和比较器排序(有参构造)。
  2. public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, Serializable
    
  3. 基于 TreeMapNavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。
  4. 此实现为基本操作(addremovecontains)提供受保证的 log(n) 时间开销。

2.TreeSet保证元素唯一和排序的原理

TreeSet集合底层数据结构是红黑树(平衡二叉树)。 第一个元素存储的时候,直接作为根节点。 从第二个开始,每个元素从根节点开始比较: 

大——就作为右孩子 (保证有序)

小——就作为左孩子 (保证有序)

相等——不做处理(保证唯一性)

 

3.两种排序(自然排序,比较器排序)

  • 自然排序:让元素所属的类中实现自然排序接口Comparerable接口 。可以在自定义类中实现Comparerable接口,重写compareTo()方法。 
  • 比较器排序:让集合的构造方法接收一个比较器Comparator接口的子类对象。可以在自定义类中实现Comparetor接口,重写compare()方法。

4.自然排序的实现

4.1 Integer和String对象都可以进行默认的TreeSet排序

public class TEST {
    public static void main(String[] args) {
        //Integer类本身已经实现了Comparable接口,重新了compare()方法。
        TreeSet<Integer> treeSet = new TreeSet<Integer>();  // 使用自然排序

        treeSet.add(10);
        treeSet.add(10);
        treeSet.add(20);
        treeSet.add(18);
        treeSet.add(14);
        treeSet.add(14);
        treeSet.add(15);
        treeSet.add(90);
        treeSet.add(1);

        for (Integer number : treeSet) {
            System.out.println(number); //输出结果: 1 10 14 15 18 20 90 (去重且排序)
        }
    }
}

4.2 对自定义对象进行 自然排序

自然排序要进行一下操作:

1.自定义类中实现  Comparable<T>接口

2.重写Comparable接口中的compareTo方法

// Student类,实现Comparable接口

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;
    }
    
    // 重写compareTo方法
    @Override
    public int compareTo(Student s) {
        //return -1; //-1表示放在红黑树的左边,即逆序输出
        //return 1;  //1表示放在红黑树的右边,即顺序输出
        //return 0;  //表示元素相同,仅存放第一个元素
        //主要条件 姓名的长度,如果姓名长度小的就放在左子树,否则放在右子树
        int num = this.name.length() - s.name.length();
        //姓名的长度相同,不代表内容相同,如果按字典顺序此 String 对象位于参数字符串之前,则比较结果为一个负整数。
        //如果按字典顺序此 String 对象位于参数字符串之后,则比较结果为一个正整数。
        //如果这两个字符串相等,则结果为 0
        int num1 = num == 0 ? this.name.compareTo(s.name) : num;  // this.name.compareTo(s.name) 这个compareTo(String str)方法已经重写,可以直接调用。
        //姓名的长度和内容相同,不代表年龄相同,所以还要判断年龄
        int num2 = num1 == 0 ? this.age - s.age : num1;
        return num2;
    }
}

// 测试类

import java.util.*;

public class TEST {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Student> ts=new TreeSet<Student>();

        //创建元素对象
        Student s1=new Student("zhangsan",20);
        Student s2=new Student("lis",22);
        Student s3=new Student("wangwu",24);
        Student s4=new Student("chenliu",26);
        Student s5=new Student("zhangsan",22);
        Student s6=new Student("qianqi",24);

        //将元素对象添加到集合对象中
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        //遍历
        for(Student s:ts){
            System.out.println(s.getName()+"-----------"+s.getAge());
        }
    }
}

//打印:
lis-----------22
qianqi-----------24
wangwu-----------24
chenliu-----------26
zhangsan-----------20
zhangsan-----------22

5.比较器排序的实现

比较器排序步骤:

1.单独创建一个比较类,这里以MyComparator为例,并且要让其继承Comparator<T>接口

2.重写Comparator接口中的compare()方法

5.1 一般比较器写法

// 自定义类: Student类
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;
    }
}

// 单独创建的一个比较类,继承Comparator<T>接口,并重写compare()方法
import java.util.Comparator;

public class MyComparator implements 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;
    }

}

// 测试类
import java.util.*;

public class TEST {
    public static void main(String[] args) {
        //创建集合对象
        //TreeSet(Comparator<? super E> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。
        TreeSet<Student> ts=new TreeSet<Student>(new MyComparator());

        //创建元素对象
        Student s1=new Student("zhangsan",20);
        Student s2=new Student("lis",22);
        Student s3=new Student("wangwu",24);
        Student s4=new Student("chenliu",26);
        Student s5=new Student("zhangsan",22);
        Student s6=new Student("qianqi",24);

        //将元素对象添加到集合对象中
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        //遍历
        for(Student s:ts){
            System.out.println(s.getName()+"-----------"+s.getAge());
        }
    }
}

// 打印结果:
lis-----------22
qianqi-----------24
wangwu-----------24
chenliu-----------26
zhangsan-----------20
zhangsan-----------22

5.2 常见比较器写法

单独创建一个MyComparator类不是特别好,可以将MyComparetor的内容直接写到主类中。开发中常用匿名内部类实现。

public class TreeSetDemo {
        public static void main(String[] args) {
        // 如果一个方法的参数是接口,那么真正要的是接口的实现类的对象
		// 而匿名内部类就可以实现这个东西
		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());
		}
	}
}

感谢:

https://blog.csdn.net/jinhuoxingkong/article/details/51191106

https://blog.csdn.net/u013317445/article/details/82181179

https://blog.csdn.net/xiaofei__/article/details/53138681

猜你喜欢

转载自blog.csdn.net/m0_38109046/article/details/88976297