JDK 源码解析 —— 集合(七)TreeSet

2020-11-13 · 芋道源码 · 转载 · · 本文共 997个字,预计阅读需要 4分钟。

转载于【芋道源码

1. 概述

本文,和 《JDK 源码解析 —— 集合(五)哈希集合 HashSet》 基本是一致的。

TreeSet ,基于 TreeSet 的 Set 实现类。在业务中,如果我们有排重+ 排序的需求,一般会考虑使用 TreeSet 。不过,貌似很少会出现排重+ 排序的双重需求。所以呢,TreeSet 反正艿艿是木有使用过。

2. 类图

TreeSet 实现的接口、继承的类,如下图所示:类图类图

对于 NavigableSetSortedMap 接口,已经添加注释,胖友可以直接点击查看。

3. 属性

TreeSet 只有一个属性,那就是 m 。代码如下:

  1. // TreeSet.java
  2. private transient NavigableMap<E,Object> m;
  • m 的 key ,存储 HashSet 的每个 key 。

  • map 的 value ,因为 TreeSet 没有 value 的需要,所以使用一个统一的 PRESENT 即可。代码如下:

    1. // TreeSet.java
    2. // Dummy value to associate with an Object in the backing Map
    3. private static final Object PRESENT = new Object();

4. 构造方法

TreeSet 一共有 5 个构造方法,代码如下:

  1. // TreeSet.java
  2. TreeSet(NavigableMap<E,Object> m) {
  3. this.m = m;
  4. }
  5. public TreeSet() {
  6. this(new TreeMap<>());
  7. }
  8. public TreeSet(Comparator<? super E> comparator) {
  9. this(new TreeMap<>(comparator));
  10. }
  11. public TreeSet(Collection<? extends E> c) {
  12. this();
  13. // 批量添加
  14. addAll(c);
  15. }
  16. public TreeSet(SortedSet<E> s) {
  17. this(s.comparator());
  18. // 批量添加
  19. addAll(s);
  20. }
  • 在构造方法中,会创建 TreeMap 对象,赋予到 m 属性。

5. 添加单个元素

#add(E e) 方法,添加单个元素。代码如下:

  1. // TreeSet.java
  2. public boolean add(E e) {
  3. return m.put(e, PRESENT)==null;
  4. }
  • m 的 value 值,就是我们看到的 PRESENT

#addAll(Collection<? extends E> c) 方法,批量添加。代码如下:

  1. // TreeSet.java
  2. public boolean addAll(Collection<? extends E> c) {
  3. // Use linear-time version if applicable
  4. // 情况一
  5. if (m.size()==0 && c.size() > 0 &&
  6. c instanceof SortedSet &&
  7. m instanceof TreeMap) {
  8. SortedSet<? extends E> set = (SortedSet<? extends E>) c;
  9. TreeMap<E,Object> map = (TreeMap<E, Object>) m;
  10. if (Objects.equals(set.comparator(), map.comparator())) {
  11. map.addAllForTreeSet(set, PRESENT);
  12. return true;
  13. }
  14. }
  15. // 情况二
  16. return super.addAll(c);
  17. }
  • 在实现上,和 TreeMap 的批量添加是一样的,对于情况一,会进行优化。

6. 移除单个元素

#remove(Object o) 方法,移除 o 对应的 value ,并返回是否成功。代码如下:

  1. // TreeSet.java
  2. public boolean remove(Object o) {
  3. return m.remove(o)==PRESENT;
  4. }

7. 查找单个元素

#contains(Object key) 方法,判断 key 是否存在。代码如下:

  1. // TreeSet.java
  2. public boolean contains(Object o) {
  3. return m.containsKey(o);
  4. }

8. 查找接近的元素

在 NavigableSet 中,定义了四个查找接近的元素:

  • #lower(E e) 方法,小于 e 的 key
  • #floor(E e) 方法,小于等于 e 的 key
  • #higher(E e) 方法,大于 e 的 key
  • #ceiling(E e) 方法,大于等于 e 的 key

我们一起来看看哈。

  1. // TreeSet.java
  2. public E lower(E e) {
  3. return m.lowerKey(e);
  4. }
  5. public E floor(E e) {
  6. return m.floorKey(e);
  7. }
  8. public E ceiling(E e) {
  9. return m.ceilingKey(e);
  10. }
  11. public E higher(E e) {
  12. return m.higherKey(e);
  13. }

9. 获得首尾的元素

#first() 方法,获得首个 key 。代码如下:

  1. // TreeSet.java
  2. public E first() {
  3. return m.firstKey();
  4. }
  • #pollFirst() 方法,获得并移除首个 key 。代码如下:

    1. // TreeSet.java
    2. public E pollFirst() {
    3. Map.Entry<E,?> e = m.pollFirstEntry();
    4. return (e == null) ? null : e.getKey();
    5. }

#last() 方法,获得尾部 key 。代码如下:

  1. // TreeSet.java
  2. public E last() {
  3. return m.lastKey();
  4. }
  • #pollLast() 方法,获得并移除尾部 key 。代码如下:

    1. // TreeSet.java
    2. public E pollLast() {
    3. Map.Entry<E,?> e = m.pollLastEntry();
    4. return (e == null) ? null : e.getKey();
    5. }

10. 清空

#clear() 方法,清空。代码如下:

  1. // TreeSet.java
  2. public void clear() {
  3. m.clear();
  4. }

11. 克隆

#clone() 方法,克隆 TreeSet 。代码如下:

  1. // TreeSet.java
  2. public Object clone() {
  3. // 克隆创建 TreeSet 对象
  4. TreeSet<E> clone;
  5. try {
  6. clone = (TreeSet<E>) super.clone();
  7. } catch (CloneNotSupportedException e) {
  8. throw new InternalError(e);
  9. }
  10. // 创建 TreeMap 对象,赋值给 clone 的 m 属性
  11. clone.m = new TreeMap<>(m);
  12. return clone;
  13. }

12. 序列化

#writeObject(ObjectOutputStream s) 方法,序列化 TreeSet 对象。代码如下:

  1. // TreeSet.java
  2. @java.io.Serial
  3. private void writeObject(java.io.ObjectOutputStream s)
  4. throws java.io.IOException {
  5. // Write out any hidden stuff
  6. // 写入非静态属性、非 transient 属性
  7. s.defaultWriteObject();
  8. // Write out Comparator
  9. // 写入比较器
  10. s.writeObject(m.comparator());
  11. // Write out size
  12. // 写入 key-value 键值对数量
  13. s.writeInt(m.size());
  14. // Write out all elements in the proper order.
  15. // 写入具体的 key-value 键值对
  16. for (E e : m.keySet())
  17. s.writeObject(e);
  18. }

13. 反序列化

#readObject(ObjectInputStream s) 方法,反序列化成 TreeSet 对象。代码如下:

  1. // TreeSet.java
  2. @java.io.Serial
  3. private void readObject(java.io.ObjectInputStream s)
  4. throws java.io.IOException, ClassNotFoundException {
  5. // Read in any hidden stuff
  6. // 读取非静态属性、非 transient 属性
  7. s.defaultReadObject();
  8. // Read in Comparator
  9. // 读取比较器
  10. @SuppressWarnings("unchecked")
  11. Comparator<? super E> c = (Comparator<? super E>) s.readObject();
  12. // Create backing TreeMap
  13. // 创建 TreeMap 对象
  14. TreeMap<E,Object> tm = new TreeMap<>(c);
  15. m = tm;
  16. // Read in size
  17. // 读取 key-value 键值对数量
  18. int size = s.readInt();
  19. // 读取具体的 key-value 键值对
  20. tm.readTreeSet(size, s, PRESENT);
  21. }
  22. // TreeMap.java
  23. void readTreeSet(int size, java.io.ObjectInputStream s, V defaultVal)
  24. throws java.io.IOException, ClassNotFoundException {
  25. buildFromSorted(size, null, s, defaultVal);
  26. }

14. 获得迭代器

  1. // TreeSet.java
  2. public Iterator<E> iterator() { // 正序 Iterator 迭代器
  3. return m.navigableKeySet().iterator();
  4. }
  5. public Iterator<E> descendingIterator() { // 倒序 Iterator 迭代器
  6. return m.descendingKeySet().iterator();
  7. }

15. 转换成 Set/Collection

  1. // TreeSet.java
  2. public NavigableSet<E> descendingSet() {
  3. return new TreeSet<>(m.descendingMap());
  4. }

16. 查找范围的元素

  1. // TreeSet.java
  2. // subSet 组
  3. public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
  4. E toElement, boolean toInclusive) {
  5. return new TreeSet<>(m.subMap(fromElement, fromInclusive,
  6. toElement, toInclusive));
  7. }
  8. public SortedSet<E> subSet(E fromElement, E toElement) {
  9. return subSet(fromElement, true, toElement, false);
  10. }
  11. // headSet 组
  12. public NavigableSet<E> headSet(E toElement, boolean inclusive) {
  13. return new TreeSet<>(m.headMap(toElement, inclusive));
  14. }
  15. public SortedSet<E> headSet(E toElement) {
  16. return headSet(toElement, false);
  17. }
  18. // tailSet 组
  19. public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
  20. return new TreeSet<>(m.tailMap(fromElement, inclusive));
  21. }
  22. public SortedSet<E> tailSet(E fromElement) {
  23. return tailSet(fromElement, true);
  24. }

彩蛋

😈 总的来说,比较简单,相信胖友一会会时间就已经看完了。

关于 TreeSet 的总结,只有一句话:TreeSet 是基于 TreeMap 的 Set 实现类。