前言
上一篇说了红黑树的实现——平衡二叉树和红黑树-CSDN博客
这一次说的是集合TreeSet的排序原理,跟红黑树有很大的联系。
目录
一、前菜
前面就有说TreeSet底层是TreeMap,TreeMap的底层是红黑树。
红黑树就是二叉查找树+维护红黑规则,也就是小的放左边,大的放右边,+红黑规则维护。
这次要说的是TreeSet的底层是怎么实现小的放左边,大的放右边。以及java包装好的基本类型包装类和String的排序规则 + 自定义类的排序规则如何设计。
1.先看结论
TreeSet底层是通过类实现的比较器来实现的,比如compareTo()方法。
2.TreeSet排序的底层源码
(1)先插入一个数据作为根
下面进入到TreeMap的put()方法,进入到compare()方法是不返回任何数值的,第一个数据只要不是null就作为红黑树的根
(2)添加后面的数据
下面这部分代码是put()方法接下来的部分,处理非根的数据,数据会进入compare()方法,第一层循环先将根作为父节点,跟父节点比较,如果比父节点小,就进入到父节点的左边,如果比父节点大就放右边,相等就丢掉。
如果进入的位置不为空,就将当前位置的节点作为父节点,继续上面的流程。直到遇到空的位置。
最后一次循环compare()返回的int数值cmp小于0,将数据放到父节点左边,反之,放到右边。
二、数值类型和Char、String的比较器
数值类型和Char、String的比较器是java包装好的,这些类实现camparable接口,并重写compareTo(添加的数据)方法
1.数值类型
以Integer,Double为例,默认按照从小到大的顺序进行排序。
x是正在添加的数据 y是正在比较的父节点
上面是Integer包装类的比较器方法,在TreeSet底层,添加一个数据时,将父节点传入到要添加的数据对象的compare()方法作为参数进行比较。如果大于父节点返回1,如果小于父节点返回-1,相等的话返回0。
2.字符和字符串
照字符在ASCII码表中的数字升序进行排序。如果是字符串的话,先比较字符串的第一个字符,如果第一个字符一样,就比较第二个字符,直到最短字符串结束。如果字符都相等,返回正在添加的字符串的长度减父节点字符串的长度。
下面是String类封装好的compareTo()方法。
三、自定义类实现comparable和comparator
自定义类要放在TreeSet中就要实现comparable或者comparator,不然代码运行会报错的。
下面介绍三种实现方法
1.自定义类实现Comparable接口
示例代码:
import java.util.TreeSet;
// 自定义类 Person 实现 Comparable 接口
class Person implements Comparable<Person> {
private String name;
private int age;
// 构造函数
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Getter 方法
public String getName() {
return name;
}
public int getAge() {
return age;
}
// 重写 compareTo 方法
@Override
public int compareTo(Person other) {
// 按年龄升序排列
return Integer.compare(this.age, other.age);
// 如果需要按名字排序,可以使用以下代码:
// return this.name.compareTo(other.name);
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
public class TreeSetExample {
public static void main(String[] args) {
// 创建 TreeSet 实例
TreeSet<Person> treeSet = new TreeSet<>();
// 添加 Person 对象
treeSet.add(new Person("Alice", 30));
treeSet.add(new Person("Bob", 25));
treeSet.add(new Person("Charlie", 35));
// 输出 TreeSet 中的 Person 对象
for (Person person : treeSet) {
System.out.println(person);
}
}
}
运行结果:
2.匿名内部类方式实现comparator接口
示例代码:
// 自定义类 Person
class Person {
private String name;
private int age;
// 构造函数
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Getter 方法
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
public class ComparatorExample {
public static void main(String[] args) {
// 使用匿名内部类按年龄排序的 TreeSet
TreeSet<Person> ageSortedSet = new TreeSet<>(new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
return Integer.compare(p1.getAge(), p2.getAge());
}
});
ageSortedSet.add(new Person("Alice", 30));
ageSortedSet.add(new Person("Bob", 25));
ageSortedSet.add(new Person("Charlie", 35));
System.out.println("按年龄排序:");
for (Person person : ageSortedSet) {
System.out.println(person);
}
// 使用匿名内部类按名字排序的 TreeSet
TreeSet<Person> nameSortedSet = new TreeSet<>(new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
return p1.getName().compareTo(p2.getName());
}
});
nameSortedSet.add(new Person("Alice", 30));
nameSortedSet.add(new Person("Bob", 25));
nameSortedSet.add(new Person("Charlie", 35));
System.out.println("\n按名字排序:");
for (Person person : nameSortedSet) {
System.out.println(person);
}
}
}
运行结果:
3.新建比较器类实现comparator接口
示例代码:
// 自定义类 Person
class Person {
private String name;
private int age;
// 构造函数
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Getter 方法
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
// 按年龄排序的比较器
class AgeComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return Integer.compare(p1.getAge(), p2.getAge());
}
}
// 按名字排序的比较器
class NameComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return p1.getName().compareTo(p2.getName());
}
}
public class ComparatorExample {
public static void main(String[] args) {
// 根据年龄排序的 TreeSet
TreeSet<Person> ageSortedSet = new TreeSet<>(new AgeComparator());
ageSortedSet.add(new Person("Alice", 30));
ageSortedSet.add(new Person("Bob", 25));
ageSortedSet.add(new Person("Charlie", 35));
System.out.println("按年龄排序:");
for (Person person : ageSortedSet) {
System.out.println(person);
}
// 根据名字排序的 TreeSet
TreeSet<Person> nameSortedSet = new TreeSet<>(new NameComparator());
nameSortedSet.add(new Person("Alice", 30));
nameSortedSet.add(new Person("Bob", 25));
nameSortedSet.add(new Person("Charlie", 35));
System.out.println("\n按名字排序:");
for (Person person : nameSortedSet) {
System.out.println(person);
}
}
}
运行结果: