本博客将从源码的角度带领大家学习TreeSet相关的知识。

一TreeSet类的定义:

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

可以看到TreeSet是继承自AbstracSet同时实现了NavigableSet,Cloneable,Serializable三个接口,其中Cloneable,Serializable这两个接口基本上是java集合框架中所有的集合类都要实现的接口。

二TreeSet中的重要属性:

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

可以看到在addAll方法中首先会判断是否传入的集合参数c是否为SortedSet或其子类且c不为空(c.size()>0),如果是则会调用addAllForTreeSet方法,否则会直接返回addAll方法的结果,关于addAll方法请参看我的博客【java集合框架源码剖析系列】java源码剖析之HashSet相关内容,因为内容相同,在此不做赘述,重点来看一下addAllForTreeSet方法:

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非常简单

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。

4TreeSet中元素的值必须是唯一的,因为TreeSet底层是基于TreeMap的,而TreeMap不允许元素key重复。

【java集合框架源码剖析系列】java源码剖析之TreeSet的更多相关文章

  1. [转载] Java集合框架之小结

    转载自http://jiangzhengjun.iteye.com/blog/553191 1.Java容器类库的简化图,下面是集合类库更加完备的图.包括抽象类和遗留构件(不包括Queue的实现): ...

  2. Java集合框架体系JCF

    Java 集合框架体系作为Java 中十分重要的一环, 在我们的日常开发中扮演者十分重要的角色, 那么什么是Java集合框架体系呢? 在Java语言中,Java语言的设计者对常用的数据结构和算法做了一 ...

  3. Java集合框架使用总结

    Java集合框架使用总结 前言:本文是对Java集合框架做了一个概括性的解说,目的是对Java集合框架体系有个总体认识,如果你想学习具体的接口和类的使用方法,请参看JavaAPI文档. 一.概述数据结 ...

  4. 【java集合框架源码剖析系列】java源码剖析之HashSet

    注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本.本博客将从源码角度带领大家学习关于HashSet的知识. 一HashSet的定义: public class HashSet&l ...

  5. 【java集合框架源码剖析系列】java源码剖析之TreeMap

    注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本.本博客将从源码角度带领大家学习关于TreeMap的知识. 一TreeMap的定义: public class TreeMap&l ...

  6. 【java集合框架源码剖析系列】java源码剖析之ArrayList

    注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本. 本博客将从源码角度带领大家学习关于ArrayList的知识. 一ArrayList类的定义: public class Arr ...

  7. 【java集合框架源码剖析系列】java源码剖析之LinkedList

    注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本. 在实际项目中LinkedList也是使用频率非常高的一种集合,本博客将从源码角度带领大家学习关于LinkedList的知识. ...

  8. 【java集合框架源码剖析系列】java源码剖析之HashMap

    前言:之所以打算写java集合框架源码剖析系列博客是因为自己反思了一下阿里内推一面的失败(估计没过,因为写此博客已距阿里巴巴一面一个星期),当时面试完之后感觉自己回答的挺好的,而且据面试官最后说的这几 ...

  9. 【java集合框架源码剖析系列】java源码剖析之java集合中的折半插入排序算法

    注:关于排序算法,博主写过[数据结构排序算法系列]数据结构八大排序算法,基本上把所有的排序算法都详细的讲解过,而之所以单独将java集合中的排序算法拿出来讲解,是因为在阿里巴巴内推面试的时候面试官问过 ...

随机推荐

  1. 安装MySQL后出现发生系统错误2或者系统找不到指定的文件

    就是出现如下图所示的情况: 上图中画横线的地方可以看出,sql服务确实安装了.出现这种情况的原因就是服务的默认目录与sql文件的安装目录不一致.这里我个人的MySQL安装路径为D:\mysql-5.7 ...

  2. jquery easyui datagrid 设置设置在选中的所有行中只选择第一行

    var row = $('#dg').datagrid('getSelected'); if ($('#dg').datagrid('getChecked').length > 1) { //将 ...

  3. EM vs REM vs PX,为什么你不应该”只用px“”

    Actually this artical is from other person's opnion ,i just put it into chinese,and this means a ver ...

  4. 关于ES7中的async/await在客户端和服务端上的实践

    一.前言 在项目中经常遇到处理异步请求的情况,面对层层的嵌套,回调显示那么苍白无力: async / await是ES7的重要特性之一,也是目前社区里公认的优秀异步解决方案,既然这样就用上吧. 二.配 ...

  5. Linux的管理类命令及其使用方法

    文件操作相关有一些命令可以帮助我们"修剪"之前看到的文件树. $touch a.txt 如果a.txt不存在,生成一个新的空文档a.txt.如果a.txt存在,那么只更改该文档的时 ...

  6. Vue nextTick 机制

    背景 我们先来看一段Vue的执行代码: export default { data () { return { msg: 0 } }, mounted () { this.msg = 1 this.m ...

  7. java.lang.NoSuchMethodException: org.apache.catalina.deploy.WebXml addServlet

    解决此异常的方法是: 删除您添加在Referenced Libraries 下的catalina.jar包, 然后删除Webapp下的部署程序,重新部署后一切正常. ? --------------- ...

  8. Go 语言接口

    Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口. 实例 /* 定义接口 */ type interface_name in ...

  9. dokcer自动化构建部署java web 基于jenkins+maven+nuxus容器

    # dokcer自动化构建部署java web 基于jenkins+maven+nuxus容器 #环境centos 7.4 docker 18.03.0-ce # nuxus,创建maven本地源(可 ...

  10. 2016移动端Android新技术综合预览--好文不多,这一篇就足够

    Csdn /Tamic 原文地址: http://blog.csdn.net/sk719887916/article/details/53525067 本文章6月份已完成(http://www.jia ...