TreeSet
是 Java 中的一个有序集合实现,它基于红黑树数据结构来存储元素,可以保持元素的自然顺序(默认情况下升序)或者根据自定义比较器来进行排序。下面是关于 TreeSet
的基本介绍、细节讨论、使用注意事项、常用方法以及一些底层实现细节。
基本介绍:
-
TreeSet
是Set
接口的实现类,它实现了一个有序的、无重复元素的集合。 -
TreeSet
中的元素是按照其自然顺序或者比较器的顺序进行排序的。 -
TreeSet
不允许存储重复的元素,每个元素在集合中只能出现一次。
细节讨论:
-
TreeSet
会根据元素的自然顺序或者自定义的比较器来进行排序。如果元素没有实现Comparable
接口并且没有提供比较器,会在运行时抛出ClassCastException
。 - 添加、删除和查找操作的时间复杂度平均为 O(log n),其中 n 是集合的大小。
-
TreeSet
不是线程安全的,如果在多线程环境下使用,需要进行适当的同步控制。
使用注意事项:
- 在使用
TreeSet
时,元素需要具有可比较性(可以用instanceOf作为防护机制),要么实现Comparable
接口,要么在构造TreeSet
时提供一个比较器。 - 自然顺序是指元素的默认顺序,如数字的升序、字符串的字典序等。如果需要不同的顺序,可以通过比较器来实现。
常用方法:
-
add(E e)
: 向集合中添加一个元素。 -
remove(Object o)
: 从集合中移除指定的元素。 -
contains(Object o)
: 判断集合是否包含指定的元素。 -
isEmpty()
: 判断集合是否为空。 -
size()
: 返回集合中元素的数量。 -
clear()
: 清空集合中的所有元素。
底层源码和底层实现:
-
TreeSet
的底层基于红黑树(Red-Black Tree)数据结构来存储元素。红黑树是一种自平衡的二叉查找树,确保树的高度始终保持在一个相对较小的范围内,从而保证了添加、删除和查找操作的高效性能。 - 红黑树的特性使得元素在树中按照顺序排列,从而实现了
TreeSet
的有序性。 - 通过自平衡的操作,红黑树能够在插入和删除元素时自动进行树的重新平衡,从而保持树的结构稳定。
总之,TreeSet
提供了一个有序的集合实现,通过底层的红黑树数据结构实现了高效的元素存储、添加、删除和查找操作。在需要保持顺序的集合场景下,可以使用 TreeSet
。
TreeSet的底层代码分析:
public class TreeSet_ {
public static void main(String[] args) {
//1. 当我们使用无参构造器,创建 TreeSet 时,仍然是无序的(存进去的和取出来的顺序不一样),但是取出来的顺序是按自然顺序排序的(自然顺序是指元素的默认顺序,如数字的升序、字符串的字典序等)
//2. 如果需要不同的顺序,可以通过比较器来实现。如:希望添加的元素,按照字符串的长度来排序
//3. 使用 TreeSet 提供的一个构造器,可以传入一个比较器(匿名内部类)
// 并指定排序规则
//简单看看源码
/*
1. 构造器把传入的比较器对象,赋给了 TreeSet 的底层的 TreeMap 的属性 this.comparator
public TreeMap(Comparator < ? super K > comparator) {
this.comparator = comparator;
}
2. 在 调用 treeSet.add("tom"), 在底层会执行到
if (cpr != null) {//cpr 就是我们的匿名内部类(对象)
do {
parent = t;
//动态绑定到我们的匿名内部类(对象)compare
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else //如果相等,即返回 0,这个 Key 就没有加入
return t.setValue(value);
} while (t != null);
}
*/
// TreeSet treeSet = new TreeSet();//默认自然排序
TreeSet treeSet = new TreeSet(new Comparator() {//自定义排序
@Override
public int compare(Object o1, Object o2) {
//下面 调用 String 的 compareTo 方法进行字符串大小比较
//让加入的元素,按照长度大小排序
//return ((String) o2).compareTo((String) o1);
return ((String) o1).length() - ((String) o2).length();
}
});
//添加数据. treeSet.add("jack");
treeSet.add("ret");//3
treeSet.add("ret1");
treeSet.add("a");
treeSet.add("abc");//3此时这个加不进去,因为此时是按字符串的长度来排序的,长度和tom相同都为3,但若是在自然排序下是可以插入进去的,因为默认的情况下是比较的两个字符串是否一样而不是比较的长度
System.out.println("treeSet=" + treeSet);
}
}