treeset为什么不能get_Java集合系列-Set系列-TreeSet

本人是工作7年的老程序员,在头条分享我对Java运用和源码、各种框架运用和源码的认识和理解,如果对您有所帮助,请持续关注。

声明:所有的文章都是自己工作之余一个字一个字码上去的,希望对学习Java的同学有所帮助,如果有理解不到位的地方,欢迎交流。

这一篇文章是集合的最后一篇文章,好多网友私信我能不能写几篇关于并发包中的文章,因为并发包中大部分类我都仔细研究过,也有自己的理解,所以接下来我计划开始对并发包中的类进行源码分析,如果对你有所帮助,希望大家持续关注,集合中还有两个面试过程中问的比较多的CopyOnWriteArrayList和CopyOnWriteArraySet,这两个属于并发包中的类,我会在接下来的文章详细讲解。ConcurrentHashMap也属于并发包中的类,因为在面试高级开发时,大部分面试官都喜欢问这个类,所以我提前用用大量的篇幅讲解了,在讲解并发包时就不在讲解这个类了,以后的文章会用大量的篇幅去讲解并发包中内容。还有好多网友建议我能不能录几个系列视频,谢谢大家的建议,我接下来也会着手录几个专题视频,关于JDK源码、spring源码,Netty源码等等吧,工作那么多年,自己对各个技术都有所了解吧,我会通过头条号把我在工作中的经历和对技术的研究分享给大家,希望对大家有所帮助吧,因为能力有限,可能有理解不到位或者错误的地方,欢迎交流指正。好多学习Java的同学都把并发包叫做JUC( java.util.concurrent的简称),所以文章出现JUC,就代表并发包。

36339b2b674eb289d68bd40ecb246189.png

上一篇文章主要介绍了Set集合的一个非常重要的类HashSet,它的底层是通过HashMap实现的,HashSet的源码是非常容易理解的,本篇文章介绍Set集合的另一个非常重要的实现:TreeSet,它与HashSet的主要区别就是TreeSet是排序的,它支持两种排序:

1:构造函数传递了实现Comparator接口的实现类2:添加的元素实现了Comparable接口

我们已经熟悉了HashSet实现原理,那TreeSet是怎样实现的呢?接下来我们就进入TreeSet里面看一看。

本篇文章的主要内容:

1:TreeSet的实例2:TreeSet的源码解析3:TreeSet与HashSet的区别

一、TreeSet的实例

第一个实例:

public static void main(String[] args) { Setset = new TreeSet<>(); set.add(10); set.add(1); set.add(10); set.add(null); set.add(2); set.add(3); set.add(20); System.out.println("TreeSet的长度:"+set.size()); System.out.println("TreeSet遍历:"); for(Integer i:set){ System.out.print(i+" "); }}

运行结果如图:

b55d7824c3d595d2a00d1a20899473b7.png

TreeSet添加null元素运行结果

第二个实例:

public static void main(String[] args) { Setset = new TreeSet<>(); set.add(10); set.add(1); set.add(10); set.add(2); set.add(3); set.add(20); System.out.println("TreeSet的长度:"+set.size()); System.out.println("TreeSet遍历:"); for(Integer i:set){ System.out.print(i+" "); }}
fc6ef08c69a967a3d1081ddb9ed84526.png

TreeSet不添加null元素

第三个实例:

public class Test { public static void main(String[] args) { Setset = new TreeSet<>(); set.add(new TreeSetClass(20)); set.add(new TreeSetClass(2)); }}//TreeSetClass没有实现Comparable接口class TreeSetClass { private Integer pro; public TreeSetClass(Integer pro) { this.pro = pro; } public Integer getPro() { return pro; } public void setPro(Integer pro) { this.pro = pro; }}

运行结果如下:

719c33ba8dbba2730f3c33617ad1855c.png

第四个实例:

public class Test { public static void main(String[] args) { Setset = new TreeSet<>(); set.add(new TreeSetClass(20)); set.add(new TreeSetClass(2)); }}//TreeSetClass实现了Comparable接口,但是泛型是Integerclass TreeSetClass implements Comparable{ private Integer pro; public TreeSetClass(Integer pro) { this.pro = pro; } public Integer getPro() { return pro; } public void setPro(Integer pro) { this.pro = pro; } @Override public int compareTo(Integer o) { return 0; }}

运行结果如下:

0b3e9e9df937b81218868776ed23fdcc.png

第五个实例:

public class Test { public static void main(String[] args) { Setset = new TreeSet<>(); set.add(new TreeSetClass(20)); set.add(new TreeSetClass(2)); for(TreeSetClass tc:set){ System.out.println(tc.getPro()); } }}//TreeSetClass实现了Comparable接口,泛型是TreeSetClassclass TreeSetClass implements Comparable{ private Integer pro; public TreeSetClass(Integer pro) { this.pro = pro; } public Integer getPro() { return pro; } public void setPro(Integer pro) { this.pro = pro; } @Override public int compareTo(TreeSetClass o) { if(this.pro>o.pro){ return 1; }else if(this.pro

运行结果如下:

74ab50631e6a9a9f9357b7a186e801c8.png

第六个实例:

public class Test { public static void main(String[] args) { Setset = new TreeSet<>(new Comparator() { @Override public int compare(TreeSetClass o1, TreeSetClass o2) { if(o1.getPro()>o2.getPro()){ return 1; }else if(o1.getPro()

运行结果如下:

54c14561cf754cc1eb010df432cd6443.png

通过上面的6个实例以及运行结果,大家能不能总结出一些关于TreeSet的特点呢?

特点1:TreeSet不能添加null值,否则会报空指针异常特点2:如果创建TreeSet时没有传递Comparator,对于自定义的对象必须实现Comparable接口,并且Comparable的泛型必须是自定义的对象。特点3:如果创建TreeSet时传递了Comparator,则自定义对象不需要实现Comparable接口。

上面TreeSet的3个特点非常的重要,大家最好理解掌握,因为TreeSet具有排序功能,所以元素要能够进行排序。接下来我们从源码角度看一下为什么TreeSet元素不能添加null值?为什么传递了Comparator后,自定义对象就无需实现Comparable接口了?为什么创建TreeSet时没有传递Comparator,自定义对象必须实现Comparable接口?带着这3个问题,我们开始今天的源码之旅。在分析源码之前,我们先看一下TreeMap的继承关系,为读懂源码打好基础。

f98b01de32c90ac36ad5cc06ff9fb0eb.png

二、TreeSet的源码解析

2.1、TreeSet的成员变量

//底层也是一个Map集合,从上面的图片可以看到NavigableMap就是TreeMapprivate transient NavigableMap m;//因为TreeSet的底层是一个TreeMap,添加的元素作为TreeMap的key,而常量PRESENT是TreeMap的valueprivate static final Object PRESENT = new Object();

从上面的两个成员变量可以看出,TreeSet的实现和HashSet类似,底层都是一个Map集合实现的,添加的元素作为Map集合的key,PRESENT作为Map的value,不过HashSet底层是通过HashMap实现的,而TreeSet由于具有排序功能,它的底层利用TreeMap这个具有排序功能的Map实现的。

2.2、TreeSet的构造函数

//第一个构造函数,没有被public修饰,只能它以及子类调用TreeSet(NavigableMap m) { this.m = m;}//第二个构造函数,调用第一个构造函数,将TreeMap赋值给成员变量mpublic TreeSet() { this(new TreeMap());}//第三个构造函数,传递了一个Comparator,然后调用第一个构造函数,然后赋值给成员变量mpublic TreeSet(Comparator super E> comparator) { this(new TreeMap<>(comparator));}//第四个构造函数,将Collection添加到TreeSet中public TreeSet(Collection extends E> c) { this(); addAll(c);}//第五个构造函数。public TreeSet(SortedSet s) { this(s.comparator()); addAll(s);}

从上面的构造函数可以看出,初始化成员变量m,也就是TreeMap,上面我总结TreeSet特点时讲到如果创建TreeSet时传递Comparator,则自定义的对象不需要实现Comparable接口。第三个构造函数就是这个作用。

2.3、TreeSet的重要方法

第一个重要的方法add

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

add方法直接调用了TreeMap的put方法,在TreeMap这一篇文章中,我对put方法做了详细的讲解,大家不太熟悉TreeMap的可以点击去观看。

更正TreeMap这一篇文章的一个不严谨的地方:

当Comparator!=null时,允许key==null.上面不是说不允许为null吗?
c54268c46ba9932ff2a1887affd40e9e.png

如图上面的说法确实有一点问题,并且在这篇文章中的举例也是有问题的,我在这两个方法并没有去比较,直接返回return 0;

//传递Comparator.public int compareTo(Object o) { return 0;}

在这篇文章我举例并不是太严禁,其实做比较在compareTo方法中不能直接返回0,所以大家不要被我误导了。你就记住,不管是TreeMap还是TreeSet,如果key==null,则迭代或者获取时会出现异常。大家对我在TreeMap这一篇文章的举例忽略就行。

所以TreeSet的排序功能是通过TreeMap实现的,而TreeMap底层是利用红黑树实现的,在TreeMap中我详细介绍了怎样排序的,对红黑树不理解的,请看红黑树1和红黑树2这两篇文章。

其他方法如下:

public boolean remove(Object o) { return m.remove(o)==PRESENT;}public boolean contains(Object o) { return m.containsKey(o);}public boolean isEmpty() { return m.isEmpty();}public int size() { return m.size();}

从上面的源码可以看出TreeSet实现机制和HashSet实现机制一样,都是调用底层的Map实现的。前面我对Map做了详细的讲解,这里就不在跟进分析了。

三、TreeSet与HashSet的异同

3.1、相同点

1:都是实现了Set集合2:元素插入的顺序和取出的顺序不一定相同3:元素不能重复

3.2、不同点

1:TreeSet自定义对象时要么在构造函数传递Comparator,要么自定义对象实现Comparable接口,否则无法比较顺序,会抛出异常。而HashSet没有这个要求。

这一篇文章主要介绍了TreeSet的内部实现原理,如果对Map集合非常的熟悉的话,对于TreeSet和HashSet是非常简单的,因为它们都是通过底层的Map集合实现的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值