JAVA源码学习之集合-TreeSet

前言

研究完了,HashSet和LinkHashSet,那么TreeSet当然也是不能错过的了。这章我们就一起研究下TreeSet的源码

正文

类的描述

public class TreeSet<E>
extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, Serializable

TreeMap 是基于NavigableSet实现的。元素使用其自然顺序排序,或由 Comparator在设定的创建时间进行排序,这两种方式取决于使用哪个构造函数。

此实现为基本操作(添加、删除和包含)提供有保证的 log(n) 时间成本。

请注意,如果要正确实现 Set 接口,则集合维护的排序(无论是否提供显式比较器)必须与 equals 一致。 (请参阅 Comparable 或 Comparator 以获取与 equals 一致的精确定义。)这是因为 Set 接口是根据 equals 操作定义的,但是 TreeSet实际是使用它的 compareTo(或 compare)方法执行所有元素比较,所以两个 被此方法视为相等的元素,从集合的角度来看,是相等的。 集合的行为是明确定义的,即使它的顺序与 equals 不一致; 它只是不遵守 Set 接口的一般约定。

请注意,此实现不是同步的。 如果多个线程同时访问一个树集,并且至少有一个线程修改了该集,则必须在外部进行同步。 这通常是通过同步一些自然封装集合的对象来完成的。 如果不存在此类对象,则应使用 Collections.synchronizedSortedSet 方法“包装”该集合。 这最好在创建时完成,以防止对集合的意外不同步访问

SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));

此类的迭代器方法返回的迭代器是快速失败的:如果在迭代器创建后的任何时间修改集合,除了通过迭代器自己的 remove 方法以外的任何方式,迭代器都会抛出 ConcurrentModificationException。 因此,面对并发修改,迭代器快速而干净地失败,而不是冒着在未来不确定的时间出现任意、非确定性行为的风险。

请注意,无法保证迭代器的快速失败行为是实时的,因为一般而言,在存在非同步并发修改的情况下不可能做出任何硬保证。 快速失败的迭代器会尽最大努力抛出 ConcurrentModificationException。 因此,编写一个依赖此异常来确保其正确性的程序是错误的:迭代器的快速失败行为应该仅用于检测错误。

构造方法

 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);
    }

通过上面的构造方法我们可以得知,TreeSet是通过TreeMap来实现的,然后大致分为指定排序方法的构造方法,和元素自然排序的构造方法,TreeMap我们在接下来的Map模块进行研究

添加

//
public boolean add(E e) {
        return m.put(e, PRESENT)==null;
   // == null的问题请参考HashSet
 }

-- 使用的是TreeMap的put方法
public V put(K key, V value) {
        Entry<K,V> t = root;
        if (t == null) { // 如果根节点为空, -- 当前袁术作为根节点
            compare(key, key); // type (and possibly null) check 

            root = new Entry<>(key, value, null); 
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        -- 通过排序进行树的循环插入节点操作
        if (cpr != null) { -- 如果排序不为空,
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value); -- 如果旧的,会替换value, 并返回旧的value
            } while (t != null);
        }
        else {
            if (key == null) -- 如果key为空会报NullPointerException
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value); --如果旧的,会替换value, 并返回旧的value
            } while (t != null);
        }
       -- 如果是新节点
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        -- 插入元素后操作树,放到HashMap中讲
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

// 返回旧的value
 public V setValue(V value) {
            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }


public  boolean addAll(Collection<? extends E> c) {
        // Use linear-time version if applicable
        if (m.size()==0 && c.size() > 0 && //当当前集合为空,且C是有序集合
            c instanceof SortedSet &&
            m instanceof TreeMap) {
            SortedSet<? extends E> set = (SortedSet<? extends E>) c;
            TreeMap<E,Object> map = (TreeMap<E, Object>) m;
            Comparator<?> cc = set.comparator();
            Comparator<? super E> mc = map.comparator();
            // 如果当前TreeMap排序和集合C的排序方法是一致的
            if (cc==mc || (cc != null && cc.equals(mc))) {
                map.addAllForTreeSet(set, PRESENT);
                return true;
            }
        }
        return super.addAll(c);
    }
/** Intended to be called only from TreeSet.addAll */
// 这个我向下继续看了但是没有看懂,我准备在TreeMap中研究, 如果有知道的同学,可以在下方留言告诉
//我,万分感谢!
void addAllForTreeSet(SortedSet<? extends K> set, V defaultVal) {
        try {
            buildFromSorted(set.size(), set.iterator(), null, defaultVal);
        } catch (java.io.IOException cannotHappen) {
        } catch (ClassNotFoundException cannotHappen) {
        }
    }

-- super,addAll(c) 调用的是 AbstractConllection的addAll
  public boolean addAll(Collection<? extends E> c) {
        boolean modified = false;
        for (E e : c)
            if (add(e)) -- 这里用的依然是TreeSet的add
                modified = true;
        return modified;
    }

   通过添加我们可以看出TreeSet是通过TreeMap的put来实现的,是通过TreeMap的key来实现不可重复的。

   TreeMap的value和HashSet一样都是指定了一个变量PRESENT

通过put的返回值 == null 判断集合元素是否重复,因为如果TreeMap的key存在的话,会返回旧值,而TreeSet的默认value都是PRESENT,所以如果元素重复,put会返回PRESENT ,如果不重复会返回null

   当未指定比较方法时,key==null时会报空指针异常,其他使用compare方法时,如果key为空,一般也会报空指针异常,所以得到结论时TreeSet的key是不可以为null的

addAll方法中特殊逻辑,在放到TreeMap中研究,如果有知道的同学,请下方留言告诉我,让我可以有参考的去研究TreeMap,然后进行验证。

移除

public boolean remove(Object o) {
        return m.remove(o)==PRESENT;
    }
//调用了map remove

public V remove(Object key) {
        Entry<K,V> p = getEntry(key);
        if (p == null)
            return null;

        V oldValue = p.value;
        deleteEntry(p); //TreeMap删除元素的操作
        return oldValue;
    }

移除是调用TreeMap的remove方法来实现的

结论

其他的方法我就不在这里写,有兴趣的同学可以自己看下

TreeSet是基于TreeMap实现的,通过TreeMap的key不可重复,和key 不能为空来实现TreeSet的两个特性。

TreeSet基本所有的方法都是通过TreeMap的方法来实现的

TreeSet不是线程安全的

上一章                                                                                                                             下一章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值