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

时间:2022-09-28 00:13:26

本博客将从源码的角度带领大家学习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. &lbrack;转载&rsqb; 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. 工作中总结的常用PHP代码

    [目录] ◆PHP常用的代码 ◆HTML常用代码 [值传递 和 引用传递] [单例模式] [魔术常量] [代码调试(自定义一个简单的debug函数)] [thinkphp多表查询] [获取客户端IP地 ...

  2. java full gc

    转自:http://blog.sina.com.cn/s/blog_7a351012010163a1.html

  3. docker镜象

    1.安装好docker后,用docker命令的时候有时候会报错:Post http:///var/run/docker.sock/v1.19/images/create?fromImage=ubunt ...

  4. JavaScript 运行机制详解:深入理解Event Loop

    Philip Roberts的演讲<Help, I'm stuck in an event-loop>,详细.完整.正确地描述JavaScript引擎的内部运行机制. 一.为什么JavaS ...

  5. 阅历&gt&semi;感悟

    1.强扭的瓜不甜.在招聘的时候,面试官看不上你,你也不用赖着要去,你去能干好工作吗?面试官通常比你更清楚这个是事情.在比如谈恋爱,姑娘有更好的目标,不喜欢你了,决定离开你了,你再怎么挽留都是没意义的, ...

  6. 多进程于多线程的区别,cpu密集型适合用什么

    多线程:在单个程序中同事运行多少个线程完成不同的工作,成为线程. 线程共享内存空间,进程的内存是独立的, 同一个进程的线程间可以直接交流: 两个进程想通信,必须通过一个中间代理来实现, 一个线程可以控 ...

  7. Window下部署Maven Nexus

    Nexus下载地址:https://www.sonatype.com/download-oss-sonatype 选择相应的版本下载后,本人下载的是nexus-2.12.0-01-bundle.zip ...

  8. 设置 SSH 通过密钥登录

    我们一般使用 PuTTY 等 SSH 客户端来远程管理 Linux 服务器.但是,一般的密码方式登录,容易有密码被暴力破解的问题.所以,一般我们会将 SSH 的端口设置为默认的 22 以外的端口,或者 ...

  9. ionic打包步骤(安卓)

    打包APP之前要做的工作: ionic resources -icon : [创建一个app图标]: 以png/psd/AI格式保存在项目目录下的:resource/android/icon.png ...

  10. 第四章 PCA降维

    目录 1. PCA降维 PCA:主成分分析(Principe conponents Analysis) 2. 维度的概念 一般认为时间的一维,而空间的维度,众说纷纭.霍金认为空间是10维的. 3. 为 ...