这个TreeSet其实和HashSet类似。HashSet底层是通过HashMap实现的,TreeSet其实底层也是通过TreeMap实现的。

简介

  TreeSet的作用是保存无重复的数据,不过还对这些数据进行了排序。

  TreeMap的底层是通过红黑树实现的,所以TreeSet底层也是通过红黑树实现的。

  TreeSet最主要的特点就是对元素进行了排序。我们对其特点进行总结一下:

    (1)TreeSet是基于TreeMap的NavigableSet实现。

    (2)TreeSet的元素存储在TreeMap中的key中,TreeMap的value是一个常量对象。

    (3)非线程安全 。

    (4)java8新增分割器spliterator() 方法。

源码分析

1.继承关系

    

2.参数变量

    

3.构造器

 // 直接使用传进来的NavigableMap存储元素
// 这里不是深拷贝,如果外面的map有增删元素也会反映到这里
// 而且, 这个方法不是public的, 说明只能给同包使用
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
} // 使用TreeMap初始化
public TreeSet() {
this(new TreeMap<E,Object>());
} // 使用带comparator的TreeMap初始化
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
} // 将集合c中的所有元素添加的TreeSet中
public TreeSet(Collection<? extends E> c) {
this();
addAll(c);
} // 将SortedSet中的所有元素添加到TreeSet中
public TreeSet(SortedSet<E> s) {
this(s.comparator());
addAll(s);
}

4 方法

 // 迭代器
public Iterator<E> iterator() {
return m.navigableKeySet().iterator();
} // 逆序迭代器
public Iterator<E> descendingIterator() {
return m.descendingKeySet().iterator();
} // 以逆序返回一个新的TreeSet
public NavigableSet<E> descendingSet() {
return new TreeSet<>(m.descendingMap());
} // 元素个数
public int size() {
return m.size();
} // 判断是否为空【本篇文章由公众号“彤哥读源码”原创】
public boolean isEmpty() {
return m.isEmpty();
} // 判断是否包含某元素
public boolean contains(Object o) {
return m.containsKey(o);
} // 添加元素, 调用map的put()方法, value为PRESENT
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();
} // 添加集合c中的所有元素
public boolean addAll(Collection<? extends E> c) {
// 满足一定条件时直接调用TreeMap的addAllForTreeSet()方法添加元素
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;
}
}
// 不满足上述条件, 调用父类的addAll()通过遍历的方式一个一个地添加元素
return super.addAll(c);
} // 子set(NavigableSet中的方法)
public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
E toElement, boolean toInclusive) {
return new TreeSet<>(m.subMap(fromElement, fromInclusive,
toElement, toInclusive));
} // 头set(NavigableSet中的方法)
public NavigableSet<E> headSet(E toElement, boolean inclusive) {
return new TreeSet<>(m.headMap(toElement, inclusive));
} // 尾set(NavigableSet中的方法)
public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
return new TreeSet<>(m.tailMap(fromElement, inclusive));
} // 子set(SortedSet接口中的方法)
public SortedSet<E> subSet(E fromElement, E toElement) {
return subSet(fromElement, true, toElement, false);
} // 头set(SortedSet接口中的方法)
public SortedSet<E> headSet(E toElement) {
return headSet(toElement, false);
} // 尾set(SortedSet接口中的方法)
public SortedSet<E> tailSet(E fromElement) {
return tailSet(fromElement, true);
} // 比较器
public Comparator<? super E> comparator() {
return m.comparator();
} // 返回最小的元素
public E first() {
return m.firstKey();
} // 返回最大的元素
public E last() {
return m.lastKey();
} // 返回小于e的最大的元素
public E lower(E e) {
return m.lowerKey(e);
} // 返回小于等于e的最大的元素
public E floor(E e) {
return m.floorKey(e);
} // 返回大于等于e的最小的元素
public E ceiling(E e) {
return m.ceilingKey(e);
} // 返回大于e的最小的元素
public E higher(E e) {
return m.higherKey(e);
} // 弹出第一个的元素,就是最小元素
public E pollFirst() {
Map.Entry<E,?> e = m.pollFirstEntry();
return (e == null) ? null : e.getKey();
}
  //弹出最后一个元素,就是最大元素
public E pollLast() {
Map.Entry<E,?> e = m.pollLastEntry();
return (e == null) ? null : e.getKey();
} // 克隆方法
@SuppressWarnings("unchecked")
public Object clone() {
TreeSet<E> clone;
try {
clone = (TreeSet<E>) super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
} clone.m = new TreeMap<>(m);
return clone;
} // 序列化写出方法
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out any hidden stuff
s.defaultWriteObject(); // Write out Comparator
s.writeObject(m.comparator()); // Write out size
s.writeInt(m.size()); // Write out all elements in the proper order.
for (E e : m.keySet())
s.writeObject(e);
} // 序列化写入方法
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden stuff
s.defaultReadObject(); // Read in Comparator
@SuppressWarnings("unchecked")
Comparator<? super E> c = (Comparator<? super E>) s.readObject(); // Create backing TreeMap
TreeMap<E,Object> tm = new TreeMap<>(c);
m = tm; // Read in size
int size = s.readInt(); tm.readTreeSet(size, s, PRESENT);
} // 可分割的迭代器
public Spliterator<E> spliterator() {
return TreeMap.keySpliteratorFor(m);
} // 序列化id
private static final long serialVersionUID = -2479143000061671589L;

总结

(1)TreeSet底层使用NavigableMap存储元素;

(2)TreeSet是有序的【本篇文章由公众号“彤哥读源码”原创】;

(3)TreeSet是非线程安全的;

(4)TreeSet实现了NavigableSet接口,而NavigableSet继承自SortedSet接口;

(5)TreeSet实现了SortedSet接口;

彩蛋

(1)通过之前的学习,我们知道TreeSet和LinkedHashSet都是有序的,那它们有何不同?

  LinkedHashSet没有实现SortedSet接口,有序性主要依赖于LinkedHashMap的有序性,所以它的有序性是指按照插入顺序保证的有序性

TreeSet实现了SortedSet接口,它的有序性主要依赖于NavigableMap的有序性,而NavigableMap又继承自SortedMap,这个接口的有序性是指按照key的自然排序保证的有序性

  而key的自然排序又有两种实现方式一种是key实现Comparable接口,一种是构造方法传入Comparator比较器。

(2)TreeSet里面真的是使用TreeMap来存储元素的吗?

  通过源码分析我们知道TreeSet里面实际上是使用NavigableMap来存储元素,虽然大部分时候这个map确实是TreeMap,但不是所有时候都是TreeMap。

  因为有一个构造方法是TreeSet(NavigableMap<E,Object> m),而且这是一个非public方法,通过调用关系我们可以发现这个构造方法都是在自己类中使用的,比如下面这个:

    public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
return new TreeSet<>(m.tailMap(fromElement, inclusive));
}

  而这个m我们姑且认为它是TreeMap,也就是调用TreeMap的tailMap()方法:

    public NavigableMap<K,V> tailMap(K fromKey, boolean inclusive) {
return new AscendingSubMap<>(this,
false, fromKey, inclusive,
true, null, true);
}

可以看到,返回的是AscendingSubMap对象,这个类的继承链是怎么样的呢? 

      

  可以看到,这个类并没有继承TreeMap,不过通过源码分析也可以看出来这个类是组合了TreeMap,也算和TreeMap有点关系,只是不是继承关系。

  所以,TreeSet的底层不完全是使用TreeMap来实现的,更准确地说,应该是NavigableMap。


   对于HashSet是用Hash表来存储数据,而TreeSet是用二叉树存储数据

   在不需要排序的时候,还是建议优先使用HashSet,因为速度更快;二叉树需要排序就免不了跳转旋转,所以速度会很慢。

java源码 -- TreeSet的更多相关文章

  1. 【java集合框架源码剖析系列】java源码剖析之TreeSet

    本博客将从源码的角度带领大家学习TreeSet相关的知识. 一TreeSet类的定义: public class TreeSet<E> extends AbstractSet<E&g ...

  2. 如何阅读Java源码 阅读java的真实体会

    刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心.   说到技术基础,我打个比 ...

  3. Android反编译(一)之反编译JAVA源码

    Android反编译(一) 之反编译JAVA源码 [目录] 1.工具 2.反编译步骤 3.实例 4.装X技巧 1.工具 1).dex反编译JAR工具  dex2jar   http://code.go ...

  4. 如何阅读Java源码

    刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动.源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧, ...

  5. Java 源码学习线路————_先JDK工具包集合_再core包,也就是String、StringBuffer等_Java IO类库

    http://www.iteye.com/topic/1113732 原则网址 Java源码初接触 如果你进行过一年左右的开发,喜欢用eclipse的debug功能.好了,你现在就有阅读源码的技术基础 ...

  6. Programming a Spider in Java 源码帖

    Programming a Spider in Java 源码帖 Listing 1: Finding the bad links (CheckLinks.java) import java.awt. ...

  7. 解密随机数生成器(二)——从java源码看线性同余算法

    Random Java中的Random类生成的是伪随机数,使用的是48-bit的种子,然后调用一个linear congruential formula线性同余方程(Donald Knuth的编程艺术 ...

  8. Java--Eclipse关联Java源码

    打开Eclipse,Window->Preferences->Java 点Edit按钮后弹出: 点Source Attachment后弹出: 选择Java安装路径下的src.zip文件即可 ...

  9. 使用JDT.AST解析java源码

    在做java源码的静态代码审计时,最基础的就是对java文件进行解析,从而获取到此java文件的相关信息: 在java文件中所存在的东西很多,很复杂,难以用相关的正则表达式去一一匹配.但是,eclip ...

随机推荐

  1. spring bean 的作用域之间有什么区别

    spring bean 的作用域之间有什么区别? spring容器中的bean可以分为五个范围.所有范围的名称都是说明的, 1.singleton:这种bean范围是默认的,这种范围确保不管接受到多个 ...

  2. 火焰图工具 SystemTap

    1. 安装 SystemTap 1. 首先安装内核开发包和调试包: # rpm -ivh kernel-debuginfo-common-($version).rpm # rpm -ivh kerne ...

  3. Vue学习手记03-路由跳转与路由嵌套

    1.路由跳转 添加一个LearnVue.vue文件, 在router->index.js中 引入import Learn from '@/components/LearnVue' 在touter ...

  4. Mysql -- The used SELECT statements have a different number of columns

    这是因为使用union的两个SQL语句产生的记录的表结构不一致. 必须是结构完全一致的记录集合才可以使用UNION. 以上就是两个表的字段不一样,导致,所以大家可以检查下. 可以 将 select * ...

  5. 【Java】 HashMap

    Java HashMap 标签(空格分隔): Java source-code hash-map 总结 HashTable的基本数据结构 Entry的hash与table的长度计算indexFor才能 ...

  6. imfilter

    图像处理函数详解——imfilter功能:对任意类型数组或多维图像进行滤波.用法:B = imfilter(A,H) B = imfilter(A,H,option1,option2,...) 或写作 ...

  7. golang 不足

    滴滴出行技术总监:关于技术选型的那些事儿 原创: 杜欢 InfoQ 2017-02-26   https://mp.weixin.qq.com/s/6EtLzMhdtQijRA7Xrn_pTg     ...

  8. android.support.design库的引用和冲突解决

    android.support.design库的引用和冲突解决 转 https://www.jianshu.com/p/2a0a2af9f2b4 最近在工程中使用到android.support.de ...

  9. PAT 甲级 1041 Be Unique (20 分)(简单,一遍过)

    1041 Be Unique (20 分)   Being unique is so important to people on Mars that even their lottery is de ...

  10. PAT 甲级 1032 Sharing (25 分)(结构体模拟链表,结构体的赋值是深拷贝)

    1032 Sharing (25 分)   To store English words, one method is to use linked lists and store a word let ...