TreeSet原理

特点

  • TreeSet 底层是采用 TreeMap 实现的一种 Set 集合;
typescript 复制代码
//和HashSet一样,PRESENT的目的是为了占位
private static final Object PRESENT = new Object();

public boolean add(E e) {
    return this.m.put(e, PRESENT) == null;
}
  • 单线程安全,多线程不安全;
  • 使用无参构造器创建TreeSet时,是无序的(存入取出顺序不一致);
csharp 复制代码
public TreeSet() {
    this((NavigableMap)(new TreeMap()));
}
  • 如果需要排序,需要指定排序规则
java 复制代码
/**
 * 有参构造函数
 * 使用带 comparator 的 TreeMap 初始化
 * @param comparator 比较器
 */
public TreeSet(Comparator<? super E> comparator) {
    this((NavigableMap)(new TreeMap(comparator)));
}

TreeSet 在存对象的时候,如果不指定自定义的比较器,那么存储的对象必须实现 Comparable 接口,以Integer类为例:

java 复制代码
public final class Integer extends Number implements Comparable<Integer> {
	
	//省略其他代码
	//......
	public int compareTo(Integer anotherInteger) {
	    return compare(this.value, anotherInteger.value);
	}
	
	public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
    }
}

TreeSet 底层插入元素的过程简述

TreeSet(TreeMap)底层是二叉树实现的,当存储元素的时候,会调用比较规则方法,将新元素和二叉树上已有的元素进行一一比较。

  • 如果要插入的元素比当前元素小、就到左子树去比较;
  • 如果比当前元素大,就到右子树去比较,直到当前元素的左或者右子树为空,就插入此元素;
  • 如果在比较过程中,出现当前元素等于要插入的元素,那么此元素不插入;

注意:基本数据类型,不需要定义比较器,只有当以树作为存储结构时,而且添加的是引用对象时,才需要定义比较器;

定义比较器的方式有两种:

实现 内部比较器Comparable 接口
typescript 复制代码
public class Student implements Comparable<Student>{
	private String name;
	private int age;
	
	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}
    //按照学生年龄排序
	@Override
	public int compareTo(Student o) {
		return this.age - o.age;
	}
}

实现 Comparable 接口的类必须实现 compareTo(Object obj) 方法,两个对象通过 compareTo 方法的返回值来比较大小 :

  • 如果当前对象 this 大于形参对象 obj 则返回正整数;
  • 如果当前对象 this 小于 形参对象 obj则返回 负整数;
  • 如果当前对象 this 等于 形参对象 obj 则返回零;
自定义外部比较器Comparator接口
csharp 复制代码
public class DemoTreeSet {
	public static void main(String[] args) {
        //匿名内部类实现比较器:首先按照年龄进行排序,如果年龄相同,我们再按照名字的长度进行排序(升序)
		TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
			@Override
			public int compare(Student o1, Student o2) {
				int num = o1.getAge() - o2.getAge();
				return num == 0 ? o1.getName().length() - o2.getName().length() : num;
			}
		});
		ts.add(new Student("张廷玉",43));
		ts.add(new Student("纳兰明珠",80));
		ts.add(new Student("索额图",79));
		ts.add(new Student("苏麻喇姑",79));
		for (Student student : ts) {
			System.out.println(student);
		}
	}
}

通过new一个匿名内部类的方式,定义一个外部比较器Comparator接口对象,重写 compare(Object o1,Object o2) 方法,比较 o1 和 o2 的大小:

  • 如果方法返回正整数,则表示 o1 大于 o2 ;
  • 如果方法返回 0 ,表示相等;
  • 如果方法返回负整数,表示o1 小于 o2 。
相关推荐
杉氧7 小时前
深入理解 Compose 重组机制:快照系统如何驱动 UI 精准刷新?
android·架构·android jetpack
召钱熏7 小时前
状态枚举正确≠渲染正确:一个语音按钮的状态机边界修复实录
android·前端
杉氧8 小时前
深度解析:Jetpack Compose 核心架构与底层原理 —— 十年安卓老兵的“破茧重生”
android·架构·android jetpack
通玄8 小时前
Jetpack Compose 入门系列(七):ViewModel 与界面状态管理
android
落魄Android在线炒饭8 小时前
Android Framework 开发技巧:android.jar 生成与系统快速编译验证
android
如此风景9 小时前
Kotlin Flow操作符学习
android·kotlin
plainGeekDev10 小时前
GreenDAO → Room
android·java·kotlin
weiggle10 小时前
第八篇:ViewModel + Compose——生产级状态管理实践
android
恋猫de小郭15 小时前
Amper 正式转正 Kotlin Toolchain ,Gradle 未来何去何从
android·前端·flutter
plainGeekDev17 小时前
ButterKnife → ViewBinding
android·java·kotlin