JDK 8 中的 TreeSet 是基于 TreeMap 实现的 有序集合,它继承自 AbstractSet 并实现了 NavigableSet 接口(NavigableSet 继承自 SortedSet)。TreeSet 的核心特性是元素唯一且按自然顺序或自定义比较器排序,并支持高效的导航操作(如查找最接近的元素)。
一、TreeSet 的核心特性
| 特性 | 说明 |
|---|---|
| 有序性 | 元素按键的自然顺序(若元素实现 Comparable)或自定义 Comparator 排序。 |
| 元素唯一 | 元素不可重复(依赖 TreeMap 键的唯一性)。 |
| 不允许 null 元素 | 若使用自然排序(无 Comparator),元素必须非 null(因 Comparable 不允许 null);若使用自定义 Comparator,可能允许 null(但需 Comparator 处理 null)。 |
| 导航方法 | 支持 ceiling()、floor()、higher()、lower() 等方法,快速查找最接近的元素。 |
| 时间复杂度 | 插入、删除、查找等操作的平均/最坏时间复杂度为 O(log n)(基于红黑树的高度)。 |
二、TreeSet 的核心结构
1. 继承与接口
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable
NavigableSet:扩展了SortedSet,增加了导航方法(如查找最近元素)和视图操作(如子集合)。AbstractSet:提供Set接口的骨架实现,减少重复代码。
2. 核心属性
TreeSet 内部通过 TreeMap 存储元素,键为集合元素,值为静态的 Object 占位符(仅用于标记存在):
// 内部 TreeMap 实例(存储元素)
private transient NavigableMap<E, Object> m;
// 值的占位符(无实际意义,仅用于 TreeMap 存储)
private static final Object PRESENT = new Object();
3. 构造方法
TreeSet 的构造方法本质是初始化内部的 TreeMap:
// 默认构造方法:使用自然排序
public TreeSet() {
this(new TreeMap<>()); // 内部 TreeMap 使用自然排序
}
// 指定 Comparator 的构造方法
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator)); // 内部 TreeMap 使用自定义比较器
}
// 从 Iterable 初始化(按自然排序或 Comparator 排序)
public TreeSet(Collection<? extends E> c) {
this();
addAll(c); // 插入所有元素,触发排序
}
// 从 SortedSet 初始化(保留排序规则)
public TreeSet(SortedSet<E> s) {
this(s.comparator()); // 继承原 SortedSet 的比较器
addAll(s); // 插入所有元素
}
三、核心方法解析
TreeSet 的方法本质是对内部 TreeMap 的封装,核心逻辑依赖 TreeMap 的实现。
1. add(E e):添加元素
add 方法通过 TreeMap.put(e, PRESENT) 实现,利用 TreeMap 键的唯一性保证元素不重复:
public boolean add(E e) {
return m.put(e, PRESENT) == null; // TreeMap 的 put 返回旧值(若存在则返回旧值,否则返回 null)
}
2. contains(Object o):判断元素是否存在
通过 TreeMap.containsKey(o) 实现,依赖 TreeMap 的键查找逻辑:
public boolean contains(Object o) {
return m.containsKey(o); // TreeMap 的 containsKey 基于红黑树查找
}
3. remove(Object o):删除元素
通过 TreeMap.remove(o) 实现,删除成功返回 true(若元素存在):
public boolean remove(Object o) {
return m.remove(o) == PRESENT; // TreeMap 的 remove 返回旧值(存在则返回 PRESENT,否则返回 null)
}
4. iterator():返回迭代器
TreeSet 的迭代器按元素的自然顺序或 Comparator 顺序遍历,通过 TreeMap.keySet().iterator() 实现:
public Iterator<E> iterator() {
return m.navigableKeySet().iterator(); // 返回 TreeMap 键集的迭代器(有序)
}
5. 导航方法(NavigableSet 接口)
TreeSet 实现了 NavigableSet 的导航方法,直接调用内部 TreeMap 的对应方法:
| 方法 | 说明 |
|---|---|
ceiling(E e) | 返回大于等于 e 的最小元素(若不存在则返回 null)。 |
floor(E e) | 返回小于等于 e 的最大元素(若不存在则返回 null)。 |
higher(E e) | 返回大于 e 的最小元素(若不存在则返回 null)。 |
lower(E e) | 返回小于 e 的最大元素(若不存在则返回 null)。 |
ceilingEntry(E e) | (内部 TreeMap 方法)返回大于等于 e 的最小键值对。 |
subSet(E fromElement, E toElement) | 返回 [fromElement, toElement) 范围内的子集合(视图)。 |
headSet(E toElement) | 返回小于 toElement 的子集合(视图)。 |
tailSet(E fromElement) | 返回大于等于 fromElement 的子集合(视图)。 |
示例:导航方法的使用
TreeSet<Integer> set = new TreeSet<>(Arrays.asList(1, 3, 5, 7, 9));
System.out.println(set.ceiling(4)); // 5(大于等于4的最小元素)
System.out.println(set.floor(6)); // 5(小于等于6的最大元素)
System.out.println(set.higher(5)); // 7(大于5的最小元素)
System.out.println(set.lower(5)); // 3(小于5的最大元素)
四、关键源码细节
1. 内部 TreeMap 的初始化
TreeSet 的构造方法通过传入 TreeMap 实例完成初始化,TreeMap 的比较器决定了 TreeSet 的排序规则:
private transient NavigableMap<E, Object> m;
// 默认构造方法调用此构造器,传入自然排序的 TreeMap
TreeSet(NavigableMap<E, Object> m) {
this.m = m;
}
2. 子集合(View)的实现
TreeSet 的 subSet、headSet、tailSet 方法返回的是 TreeSet 的视图(View),共享原 TreeSet 的底层 TreeMap,因此对视图的修改会直接影响原集合:
public NavigableSet<E> subSet(E fromElement, E toElement) {
return new TreeSet<>(m.subMap(fromElement, true, toElement, false));
}
public NavigableSet<E> headSet(E toElement) {
return new TreeSet<>(m.headMap(toElement, false));
}
public NavigableSet<E> tailSet(E fromElement) {
return new TreeSet<>(m.tailMap(fromElement, true));
}
TreeMap.subMap等方法返回的是SubMap内部类实例,通过限制键的范围实现视图效果。
五、红黑树的依赖(TreeMap 的底层逻辑)
TreeSet 的有序性和高效操作最终依赖于 TreeMap 内部的红黑树实现。红黑树通过以下机制保证平衡:
- 节点颜色:每个节点标记为红色或黑色,根节点和叶子节点(
NIL)为黑色。 - 平衡规则:无连续红色节点,任意路径的黑节点数量相同。
- 旋转与变色:插入或删除节点后,通过左旋、右旋和变色操作恢复平衡。
六、注意事项
- 元素必须可比较:若使用自然排序,元素必须实现
Comparable接口(如Integer、String),否则插入时会抛出ClassCastException。TreeSet<Person> set = new TreeSet<>(); // Person 未实现 Comparable,编译错误! - null 元素的限制:自然排序下
TreeSet不允许null元素(Comparable接口禁止null);自定义Comparator若允许null,需显式处理(否则可能抛出NullPointerException)。 - 性能对比:
TreeSet的插入、删除、查找时间复杂度为O(log n),适合需要有序性和导航的场景;HashSet的平均时间复杂度为O(1),适合仅需快速查找的场景。
七、总结
TreeSet 是基于 TreeMap 实现的有序集合,核心优势是元素唯一和有序性,并支持高效的导航操作。其内部通过 TreeMap 存储元素(键为集合元素,值为占位符),排序规则由 TreeMap 的比较器决定。实际开发中,若需要有序集合且需频繁进行范围查询或导航操作,TreeSet 是首选;若只需快速去重或查找,HashSet 更高效。
483

被折叠的 条评论
为什么被折叠?



