TreeSet源码解析以及与HashSet和HashMap的区别

552 阅读4分钟

继承关系

1.实现Serializable接口,即支持序列化。

2.TreeSet实现了Cloneable接口,意味着它能被克隆。

3.实现Iterable接口,即能用foreach使用迭代器遍历得到集合元素。

4.TreeSet实现了NavigableSet接口,意味着它支持一系列的导航方法。比如查找与指定目标最匹配项。

5.继承AbstractSet,AbstractSet实现set,所以它是一个Set集合,不包含满足element1.eauqals(element2)的元素树,

不重复,并且最多包含一个null。

基本属性

private transient NavigableMap<E,Object> m;
 	private static final Object PRESENT = new Object();

    

构造方法及初始化

1.使用NavigableMap的构造方法,底层是map

	/**
     * Constructs a set backed by the specified navigable map.
     * 构造由指定的可导航映射支持的集合。
     */
    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }

2.默认构造函数。使用该构造函数,TreeSet中的元素按照自然排序进行排列。

底层使用的TreeMap

public TreeSet() {
        this(new TreeMap<E,Object>());
    }

3.传入comparator的构造方法

// 以定制排序方式创建一个新的 TreeMap,
// 根据该 TreeSet 创建一个 TreeSet,
// 使用该 TreeMap 的 key 来保存 Set 集合的元素
public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }

4.调用方式2构造器创建一个 TreeSet,底层以 TreeMap 保存集合元素

public TreeSet(Collection<? extends E> c) {
        this();
        //向 TreeSet 中添加 Collection 集合 c 里的所有元素
        addAll(c);
    }

5.调用方式3构造器创建一个 TreeSet

public TreeSet(SortedSet<E> s) {
        this(s.comparator());
        //向 TreeSet 中添加 SortedSet 集合 s 里的所有元素
        addAll(s);
    }

小结

TreeSet是基于TreeMap实现的。

TreeSet中的元素支持2种排序方式:自然排序或者根据创建TreeSet 时提供的 Comparator 进行排序。这取决于使用的构造方法。

TreeSet为基本操作(add、remove 和 contains)提供受保证的log(n)时间开销。

TreeSet是非同步的。 它的iterator方法返回的迭代器是fail-fast的。

常用方法

add

值都是同一个PRESENT(Object), 底层还是调用的map的put方法,这里会调用TreeMap的put 方法

如果不存在则添加,如果存在这样的元素则不添加

public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }

addAll

	// 将集合c中的全部元素添加到TreeSet中
    public  boolean addAll(Collection<? extends E> c) {
        // Use linear-time version if applicable
        if (m.size()==0 && c.size() > 0 &&
            c instanceof SortedSet &&
            m instanceof TreeMap) {
            SortedSet<? extends E> set = (SortedSet<? extends E>) c;
            TreeMap<E,Object> map = (TreeMap<E, Object>) m;
            Comparator<? super E> cc = (Comparator<? super E>) set.comparator();
            Comparator<? super E> mc = map.comparator();
            if (cc==mc || (cc != null && cc.equals(mc))) {
                map.addAllForTreeSet(set, PRESENT);//还是调用TreeMap中的方法实现的
                return true;
            }
        }
        return super.addAll(c);
    }

remove

由于之前添加的值都是PRESENT(Object),所以删除的时候

调用map 的删除方法,成功的返回值也会是PRESENT(Object),所以会是相等的。

返回ture,删除失败则不相等,返回false。

public boolean remove(Object o) {
        return m.remove(o)==PRESENT;
    }

clear

调用map的clear方法,直接清除所有的键

public void clear() {
        m.clear();
    }

小结

还提供了一系列的方法,比如:

	// 返回TreeSet的顺序排列的迭代器。
    // TreeSet是TreeMap实现的,所以这里实际上时返回TreeMap的“键集”对应的迭代器
    public Iterator<E> iterator() {
        return m.navigableKeySet().iterator();
    }

    // 返回TreeSet的逆序排列的迭代器。
    // 同上这里实际上时返回TreeMap的“键集”对应的迭代器
    public Iterator<E> descendingIterator() {
        return m.descendingKeySet().iterator();
    }

总结

1.不能有重复的元素;

2.具有排序功能;

3.TreeSet中的元素必须实现Comparable接口并重写compareTo()方法,TreeSet判断元素是否重复 、以及确定元素的顺序 靠的都是这个方法;

4.对于java类库中定义的类,TreeSet可以直接对其进行存储,如String,Integer等,因为这些类已经实现了Comparable接口);

5.对于自定义类,如果不做适当的处理,TreeSet中只能存储一个该类型的对象实例,否则无法判断是否重复。

6.依赖于TreeMap,底层是TreeMap。

7.相对HashSet,TreeSet的优势是有序,劣势是相对读取慢。

根据不同的场景选择不同的集合。

TreeSet 和 HashSet比较

相同点:

都是唯一不重复的Set集合。

不同点:

底层结构

HashSet是用Hash表来存储数据,而TreeSet是用二叉平衡树来存储数据。

功能上

由于TreeSet是有序的Set,可以使用SortedSet。接口的first()、last()等方法。

但由于要排序,势必要影响速度。

所以,在不需要顺序的情况下,使用HashSet,这方面使用Hash表存储数据的HashSet在速度上更胜一筹。

如果需要顺序则使用TreeSet。

TreeSet 和 HashMap比较

相同点

1.TreeMap和TreeSet都是有序的集合。

2.TreeMap和TreeSet都是非同步集合,因此他们不能在多线程之间共享,不过可以使用方法Collections.synchroinzedMap()来实现同步。

3.运行速度都要比Hash集合慢,他们内部对元素的操作时间复杂度为O(logN),而HashMap/HashSet则为O(1)。

不同点

1.实现的接口不同TreeSet实现Set接口,而TreeMap实现Map接口。

2.TreeSet只存储一个对象,而TreeMap存储两个对象Key和Value(仅仅key对象有序)。

3.TreeSet中不能有重复对象,而TreeMap中可以存在。

写在后面的话

love all, trust a few, do wrong to none .

下集预告

提到分布式,就不可避免的会考虑到分布式锁,而它的实现方式就显得尤为重要。

所以下期就是分布式锁的实现方式。