Java中的TreeSet

Posted 卯毛的妙♂妙屋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中的TreeSet相关的知识,希望对你有一定的参考价值。

#TreeSet特点: 保证元素唯一 可以给元素进行排序 没有索引,不能用普通for循环, 查找效率高 结构为二叉排序树(也叫二叉查找树)

#自然排序Comparable:

自然排序要求类实现了自然排序接口 这里出现异常的原因是我们的Aniki并没有实现Comparable接口,TreeSet并不知道如何排序所以出问题。

我们给Aniki类添加Comparable接口,必须重写compareTo方法,当return 0的时候: 输出只有: 这是因为第一个元素添加的时候是不需要跟别人比较的,第二个元素的时候是需要跟别人比较的,会按照我们给的排序规则比较, 而我们给的规则是compareTo方法给的是0,当返回值是0的时候,会认为第二个元素与第一个元素是同一个元素。所以第二个元素添加失败, 同理第三个也失败。 当compareTo方法return 1(正数)的时候,会按照输入的顺序输出,也就是认为我们输入的顺序是升序。 当compareTo方法return -1(负数)的时候,会按照与输入相反的顺序输出,也就是降序。

#compareTo的自定义重写

我们要想要按照Age排列,要这样写

是为什么!?

##原因:

##分析: 注意:以下文字中会出现二叉树,左儿子,右儿子,前序遍历等词汇,请各位同学在《数据结构》中查找含义。

以上图为例,我们的"99"是第三个要传入的元素,

"99"进入add(),被调入put,然后我们的"99"赋值给K类型的key,

然后我们看到了建立root的过程,root就是二叉树的根,指向树根的指针是t(熟悉的C语言表示形式)。

然后是判断t是否为空,也就是判断根位置有没有元素,如果是空的就把当前的key作为根,

t以后会作为当前位置指针在循环里继续重复这种判断。

可想而知第一个add进来的"100",在这里被作为了根root,(这一步非常关键,

"100"也是之后每次新元素添加的时候,调用下面compareTo(参数)中do——while循环的第一轮的参数,

第一轮循环回让新元素判为处于根节点左子树或者右子树中,第二轮循环会让根节点左儿子或者右儿子

为根,再以新的根持续判断,但是唯一不变的是每个元素第一次进来都会先与整个二叉树的根root作比较)

与我们"99"无关,我们是第三个进来的元素,

然后看到了一个conparator构造器的判断语句,毫无疑问我们并没有new一个构造器,我们使用了无参的构造方法,

因此我们的构造器cpr是null,不进入这个if语句。

来到了else部分,判断了我们存储"99"的key是否是null,是的话抛出空指针异常,我们的key不空。

之后是一个类型转换,我们的key元素是K类型,K类型继承不继承Comparable不知道,但是我们add(99)中使用的类型

毫无疑问是implements Comparable的,否则我们会出现之前的异常,原因是:Aniki并没有实现Comparable接口

因此我们可以强制类型转换为Comparable的k,这里是多态的向上转型。

然后我们就进入了构建二叉树的do-while循环(循环内的含义参考《数据结构》)这颗二叉树的元素从左往右依次增大。

我们先与根节点比较,再次提一下这个根节点是我们第一个add的"100",当前的t是指向root根的指针,t.key就是100,

这里二叉树构建的依据是,compareTo(100)方法的返回值int类型的cmp,而我们就可以在这里做手脚。

k是我们转换好的key,"99",是我们的"99"调用的compareTo(100),因此this指的是新传入值为"99"的变量。

在我们的哲♂学例子,更改compareTo(参数)方法,来概变二叉树的真实构建情况, Age从小到大如图:

我们比较Aniki年龄来排序的时候,

“k”是我们的第二个元素Bili的Age——"50",而根root,也就是“t.key”,是我们第一个元素Van的Age——"51"。

“k”调用compareTo(51),那么“k”就是this,this.Age就是Bili的Age——"50"

aniki.Age就是Van的Age——"51"

return this.Age - aniki.Age;这句话的意思就成了

return Bili.Age - Van.Age;结果是-1,负数,Bili进入根Van的左儿子,前序遍历的结果是年龄从小到大。

因此如果我们想从大到小遍历,那么我们写:

return aniki.Age - this.Age;结果是:+1,Bili被放到Van的右儿子,前序遍历的结果是从大到小。

Age从大到小:

#Comparator比较器排序:

我们在新建TreeSet的时候,可以不执着于使用自然排序(实现Comparable接口然后重写compareTo方法),我们可以使用 Comparator比较器来完成排序(此时就不会使用自然排序)。

##Comparator比较器使用方法:

我们新建一个TreeSet并且这样new一个Comparator比较器,然后使用匿名内部类重写compare方法, 简单的修改参数含义,第一个参数是新元素,第二个元素就是我们之前提过的(t指针)元素,它在do——while第一轮中是根节点。 一开始默认return 0,与compareTo(){return 0;}的时候是一样的。

##我们来讲解下原理: 根据上图的例子来讲, 我们new出一个Comparator之后,这个Comparator会被传入final最终态的comparator变量中,之后我们通过 这个comparator变量来使用比较器。

我们以("王五",15)这个元素来看整个过程,("王五",15)被add方法调用的put方法中,值传递给了K类型的key。

然后与之前一样,第一个add进TreeSet来的元素("张三",13)被当作根节点。只不过这次我们判断有没有comparator时,

我们具备了comparator。于是进入do——while循环,这里构造器调用compare(key,t.key),

第一个位置是当前传入的元素,第二个位置是指针指向的节点,与之前的compareTo(参数)方法大同小异。

只不过compareTo是k(值为key的变量)调用的,k.compareTo(t.key)方法中,

this.key就是compare(key,t.key)中的key,t.key的话两个方法里指的都是当前被比较的节点。

因此我们: 含义相同,结果都是:

以上是关于Java中的TreeSet的主要内容,如果未能解决你的问题,请参考以下文章

java中collection是啥?怎么理解?怎么使用?

Java中Set集合怎样用for遍历?

ht-7 treeSet特性

函数入参使用指针和引用的区别

Java 9 中的 Maven 更新,Eclipse 中的 Java 8 中的 Maven 编译

关于JAVA 中的DOM操作