TreeSet源码解析和设计思路
一、继承体系
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
在Java JDK 1.8中,TreeSet
类的源码继承结构如下:
-
java.util.TreeSet<E>:
TreeSet
是基于有序树的集合类,它实现了NavigableSet
接口,提供了基于红黑树的集合实现。
通过继承AbstractSet
和实现NavigableSet
接口,TreeSet
获得了Set
接口的基本实现,并获得了导航集特定操作的支持。
TreeSet
还实现了Cloneable
接口和Serializable
接口,支持克隆和序列化。 -
java.util.AbstractSet<E>:
AbstractSet
是实现了Set
接口的抽象类,它进一步扩展了AbstractCollection
,提供了Set
接口的部分实现。
AbstractSet
中包含了Set
接口的相关方法的实现,简化了具体Set
实现类的开发。 -
java.util.AbstractCollection<E>:
AbstractCollection
是实现了Collection
接口的抽象类,定义了一些集合操作的通用实现。
AbstractCollection
提供了大部分Collection
接口的实现,使得继承它的子类(如AbstractList
、AbstractSet
等)可以更方便地实现集合类。
TreeSet
通过继承AbstractSet
和实现NavigableSet
接口,结合了抽象类和接口的优势,提供了基于有序树的集合实现,并支持克隆和序列化功能。
二、TreeSet全局变量分析
/**
* The backing map.
*/
private transient NavigableMap<E,Object> m;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
-
private transient NavigableMap<E, Object> m;
- 通过使用
NavigableMap
作为底层数据结构,TreeSet
实现了有序集合的特性,并提供了诸如查找最小/最大元素、子集操作等导航集(NavigableSet
)的功能。
- 通过使用
-
private static final Object PRESENT = new Object();
- 这是一个私有的静态常量,用于作为
m
中的值的占位符。由于TreeSet
底层使用NavigableMap
来存储元素,而NavigableMap
实际上是基于TreeMap
实现的,TreeMap
要求键值对都是存储的,不能只存储键而不存储值,因此需要一个占位符来占据值的位置。 - 在
TreeSet
中,所有元素都被存储为NavigableMap
的键,而值则被设置为PRESENT
,从而在实际存储过程中,值并不会真正占据内存空间,作为值的占位符。
- 这是一个私有的静态常量,用于作为
三、初始化对象
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
public TreeSet() {
this(new TreeMap<E,Object>());
}
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
public TreeSet(Collection<? extends E> c) {
this();
addAll(c);
}
public TreeSet(SortedSet<E> s) {
this(s.comparator());
addAll(s);
}
-
public TreeSet():
- 创建一个空的
TreeSet
,使用默认的自然顺序进行排序。此构造函数要求集合中插入的所有元素都必须实现Comparable
接口,并且这些元素必须是相互可比较的。
- 创建一个空的
-
public TreeSet(Comparator<? super E> comparator):
- 这个构造函数创建一个空的
TreeSet
,根据指定的比较器进行排序。要求插入到集合中的所有元素必须能够通过指定的比较器相互比较。
- 这个构造函数创建一个空的
-
public TreeSet(Collection<? extends E> c):
- 这个构造函数创建一个包含指定集合中元素的新
TreeSet
,按照元素的自然顺序排序。要求集合中插入的所有元素都必须实现Comparable
接口,并且这些元素必须是相互可比较的。
- 这个构造函数创建一个包含指定集合中元素的新
-
public TreeSet(SortedSet s):
- 这个构造函数创建一个包含与指定排序集相同元素和相同排序方式的新
TreeSet
。 - 使用指定排序集的比较器来初始化新的
TreeSet
,并将指定排序集中的所有元素添加到新集合中。
- 这个构造函数创建一个包含与指定排序集相同元素和相同排序方式的新
四、add添加方法
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
组合TreeMap的put方法
五、TreeSet的优点
-
有序性:
TreeSet
是基于红黑树实现的有序集合,可以保持元素的顺序排列。这使得在遍历、查询范围或搜索最小/最大元素时更高效。
-
唯一性:
TreeSet
中不允许重复元素存在,确保所有元素都是唯一的。如果有出现重复,默认覆盖
-
可导航性:
- 作为
NavigableSet
接口的实现类,TreeSet
提供了很多导航集操作。例如,获取子集、查找最接近给定元素的元素等。
- 作为
-
基于比较器的排序:
- 可以根据自然顺序或指定的比较器对元素进行排序,使得
TreeSet
能够适应不同的排序需求。
- 可以根据自然顺序或指定的比较器对元素进行排序,使得
六、TreeSet的缺点
-
内存占用:
- 由于
TreeSet
是基于红黑树实现的,每个节点都需要额外的空间来存储左右子节点和颜色信息,可能会占用较多的内存空间。
- 由于
-
不支持并发:
TreeSet
不是线程安全的,如果在多线程环境下同时修改TreeSet
,可能会导致不确定的结果。需要显式地使用诸如Collections.synchronizedSortedSet()
等方法来使其支持并发操作。
-
要求元素可比较:
- 在默认情况下,
TreeSet
要求插入的元素必须实现Comparable
接口,或者在构造函数中提供比较器。这限制了不能直接存储不可比较的对象。
- 在默认情况下,
七、HashSet和TreeSet的总结
相同点
-
不允许重复元素:
- 无论是
HashSet
还是TreeSet
,它们都不允许集合中存在重复的元素,确保了集合中的元素唯一性。
- 无论是
-
都不是线程安全的:
HashSet
和TreeSet
都不是线程安全的,如果在多线程环境下使用,需要额外考虑线程安全性。
不同点
-
底层数据结构不同:
HashSet
使用哈希表来存储元素,具有 O(1) 的平均时间复杂度;而TreeSet
基于红黑树实现,具有 O(log n) 的时间复杂度。
-
元素排序方式不同:
HashSet
不维护元素的顺序,元素在集合中是无序存放的;而TreeSet
根据元素的自然顺序或指定的比较器维护元素的有序性。
原文地址:https://blog.csdn.net/weixin_44716935/article/details/136799351
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!