JDK 8 TreeSet 详解及详细源码展示

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)的实现

TreeSetsubSetheadSettailSet 方法返回的是 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)为黑色。
  • 平衡规则:无连续红色节点,任意路径的黑节点数量相同。
  • 旋转与变色:插入或删除节点后,通过左旋、右旋和变色操作恢复平衡。

六、注意事项

  1. 元素必须可比较:若使用自然排序,元素必须实现 Comparable 接口(如 IntegerString),否则插入时会抛出 ClassCastException
    TreeSet<Person> set = new TreeSet<>(); // Person 未实现 Comparable,编译错误!
    
  2. null 元素的限制:自然排序下 TreeSet 不允许 null 元素(Comparable 接口禁止 null);自定义 Comparator 若允许 null,需显式处理(否则可能抛出 NullPointerException)。
  3. 性能对比TreeSet 的插入、删除、查找时间复杂度为 O(log n),适合需要有序性和导航的场景;HashSet 的平均时间复杂度为 O(1),适合仅需快速查找的场景。

七、总结

TreeSet 是基于 TreeMap 实现的有序集合,核心优势是元素唯一有序性,并支持高效的导航操作。其内部通过 TreeMap 存储元素(键为集合元素,值为占位符),排序规则由 TreeMap 的比较器决定。实际开发中,若需要有序集合且需频繁进行范围查询或导航操作,TreeSet 是首选;若只需快速去重或查找,HashSet 更高效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值