本博客將從源碼的角度帶領你們學習TreeSet相關的知識。java
一TreeSet類的定義:安全
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable能夠看到TreeSet是繼承自AbstracSet同時實現了NavigableSet,Cloneable,Serializable三個接口,其中Cloneable,Serializable這兩個接口基本上是java集合框架中全部的集合類都要實現的接口。
二TreeSet中的重要屬性:app
private transient NavigableMap<E,Object> m; private static final Object PRESENT = new Object();能夠看到第一個屬性是NavigableMap接口,該接口是TreeMap的父接口,即TreeMap實現了該接口,據此咱們能夠推測TreeSet的底層是基於TreeMap的,這一點稍後咱們將在TreeSet的構造器中更清楚的看到。第二個參數是以Object對象,與HashSet中的這個屬性徹底相同,即TreeSet雖然底層是基於TreeMap的,可是一樣只是用來保存Key而Value值所有爲默認值PRESENT。
三TreeSet內部實現原理:咱們來看一下TreeSet的構造器:框架
TreeSet(NavigableMap<E,Object> m) { this.m = m; } public TreeSet() { this(new TreeMap<E,Object>()); } public TreeSet(Collection<? extends E> c) { this(); addAll(c); } public TreeSet(Collection<? extends E> c) { this(); addAll(c); } public TreeSet(SortedSet<E> s) { this(s.comparator()); addAll(s); }能夠看到共5個構造器,其中第一個構造器是私有的,即對外不公開的,而在第二個默認的無參數的構造器中調用了第一個構造器,且能夠清楚的看到在這個構造器中建立了一個TreeMap對象做爲參數傳給第一個構造器,這說明咱們上面的推測:TreeSet底層是基於TreeMap的是正確的。另外餘下的幾個構造器中能夠看到當用一個集合做爲參數去構造一個TreeSet的時候,都是調用addAll這個方法,咱們來看一下其源碼:
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<?> cc = set.comparator(); Comparator<? super E> mc = map.comparator(); if (cc==mc || (cc != null && cc.equals(mc))) { map.addAllForTreeSet(set, PRESENT); return true; } } return super.addAll(c); } public boolean addAll(Collection<? extends E> c) { boolean modified = false; for (E e : c) if (add(e)) modified = true; return modified; } public boolean add(E e) { return m.put(e, PRESENT)==null; }
void addAllForTreeSet(SortedSet<? extends K> set, V defaultVal) { try { buildFromSorted(set.size(), set.iterator(), null, defaultVal); } catch (java.io.IOException cannotHappen) { } catch (ClassNotFoundException cannotHappen) { } } private void buildFromSorted(int size, Iterator<?> it, java.io.ObjectInputStream str, V defaultVal) throws java.io.IOException, ClassNotFoundException { this.size = size; root = buildFromSorted(0, 0, size-1, computeRedLevel(size), it, str, defaultVal); }能夠看到在addAllForTreeSet方法中調用了buildFromSorted(int size, Iterator<?> it, java.io.ObjectInputStream str,V defaultVal),該方法的做用便是在線性時間內對數據進行排序(Linear time tree building algorithm from sorted data),看到這裏咱們就明白TreeSet排序的原理了,即當使用一個TreeMap集合做爲參數構造一個TreeSet的時候,TreeSet會將Map中的元素先排序,而後將排序後的元素add到TreeSet中。也就是說TreeSet中的元素都是排過序的,另外正由於存在排序過程,因此TreeSet不容許插入null值,由於null值不能排序。
四TreeSet中的重要方法:學習
<strong> </strong>public boolean add(E e) { return m.put(e, PRESENT)==null; } public boolean remove(Object o) { return m.remove(o)==PRESENT; } public void clear() { m.clear(); } public boolean contains(Object o) { return m.containsKey(o); } public E first() { return m.firstKey(); } public E last() { return m.lastKey(); }能夠看到TreeSet中與TreeMap中同名的方法所有都是調用的TreeMap中的方法來實現的,其中add方法在調用TreeMap的put方法時第二個參數傳入的是固定值PRESENT,一個Object類型對象。
五總結:通過前面TreeMap的源碼剖析能夠看到TreeSet很是簡單ui
1TreeSet底層是基於TreeMap的(而TreeMap是基於紅黑樹的),可是僅僅用來保存Key,而不保存Value,由於TreeSet的add()方法在調用TreeMap的put方法的時候第二個參數傳入的都是PRESENT這個固定的Object對象。
2能夠看到TreeSet中的add與remove等方法均無synchronized關鍵字修飾,即TreeSet不是線程安全的,若是要使用同步的TreeSet須要使用Collections集合類的靜態方法,即Set s=Collections.synchronizedSet(new TreeSet());
3TreeSet中的元素是自動排好序的,插入的值不容許爲null。this
4TreeSet中元素的值必須是惟一的,由於TreeSet底層是基於TreeMap的,而TreeMap不容許元素key重複。.net