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 。
相关推荐
2501_9151063220 分钟前
全面理解 iOS 帧率,构建从渲染到系统行为的多工具协同流畅度分析体系
android·ios·小程序·https·uni-app·iphone·webview
繁星星繁29 分钟前
【Mysql】数据库基础
android·数据库·mysql
李坤林36 分钟前
Android 12 中 App 与 SurfaceFlinger(SF)的 Vsync 通信机制
android·surfaceflinger
高远-临客37 分钟前
unity IL2CPP模式下中使用UMP插件打包后无法播放视频监控报错问题解决方案
android·unity·音视频
装不满的克莱因瓶1 小时前
Windows下安装Dart
android·flutter·dart·移动端
Yao_YongChao1 小时前
adb wifi连接Android手机
android·adb·智能手机·无线连接手机·wifi连接手机
安果移不动1 小时前
git Cherry-Pick合并分支上的某些commits-》Android studio
android·git·android studio
灵感菇_2 小时前
Android Broadcast全面解析
android·广播·四大组件
byc2 小时前
Android 存储目录<内部存储,外部存储app专属,外部存储公共>
android·面试
RollingPin2 小时前
React Native与Flutter的对比
android·flutter·react native·ios·js·移动端·跨平台开发