Java基础day15笔记:TreeSet|二叉树|实现Comparator方式排序|泛型|泛型类|泛型方法|静态方法泛型|泛型接口

2019/7/12  14:15二刷留念


    01-集合框架(TreeSet)

        接下来说Set集合中的TreeSet。

        |——Set:元素是无序(存入和取出的顺序不一定一致),元素不可以重复。(想重复找List,不重复找Set)

                |——HashSet:底层数据结构是哈希表。

                    HashSet是如何保证元素唯一性的呢?

                    是通过元素的两个方法,hashCode和equals来完成。

                    如果元素的HashCode值相同,才会判断equals是否为true。

                    如果元素的hashcode值不同,不会调用equals。

                |——TreeSet:可以对Set集合中的元素进行排序。

        Set集合是无序的,这点有点缺憾,于是TreeSet就来弥补这个问题。

        先写一下试试:

        运行:

        我们发现,虽然它打印的顺序和我们存的顺序不一样,但是也是有自己的固定顺序的。

    02-集合框架(TreeSet存储自定义对象)

        需求:

        往TreeSet集合中存储自定义对象学生。

        想按照学生的年龄进行排序。

        Student类:       

        主函数: 

        但是运行的时候发现报错了:

        我们试着改一下,只存入一个元素:

        运行:

        发现成功了。我们再存两个试试:

        发现又挂掉了:

        这是为什么呢?

        我们找到java.lang包里的Comparable接口。

        到这里我们就大概明白了。

        TreeSet集合可以实现排序,可是按照什么方式排,我们并没有告诉它。而这个例子中的学生类,根本不具备比较性。而我们必须要让元素具备比较性,TreeSet集合才能帮我们排序。这个时候我们找到了Comparable接口,只要实现这个接口,学生类就具备了比较性。

        这个接口中只有一个方法:

        点进去看看:

        其中指定对象就是传进来的o,而此对象是调用这个方法的this。

        让学生类实现Comparable接口,并重写compareTo方法:

        运行:

        我们再输出一下它的比较过程,看看都和谁比过:

        我们发现,CompareTo方法会自动被调用,底层在调用这个方法,当然这个类首先得实现Comparable接口。符合我们的规则,当然就可以调用子类实现的方法。

        排序成功了,可是还存在一个小问题,当出现同年龄而不同名的元素时:

        运行:

        我们发现,lisi08没有被存进来。

        为什么呢?

        我们看一下重写的compareTo方法,它是这么写的:

        当两者年龄相同时,返回0。

        再看看compareTo方法的详细信息:

        返回0意味着此对象等于指定对象,也就是说这两个对象相当于相同对象。

        这就是lisi08没有存进来的原因,程序将它们识别为相同对象了。

        我们的修改思路是,若年龄相同,则继续判断姓名是否相同。

        String类中有个compareTo方法:

        String类它本身实现了Comparable,Java中很多类都具备比较性:

        我们调用一下这个方法(因为都是实现了Comparable接口,所以返回的也是正数、负数或0):

        运行:

        记住,排序时,当主要条件相同时,一定要判断一下次要条件。

    03-集合框架(二叉树)

        那么TreeSet底层的数据结构是怎样的呢?

        排序无非就是比较,元素越多,有可能互相比较的次数越多,这个时候效率就没那么高了。为了优化这个底层,TreeSet用了一个比较特殊的数据结构。

        用一个例子画了张二叉树的图,图上标的数字是取出数据的顺序:

        下面这个例子中:

        我们发现它存进和取出的顺序是一样的。

        下面画一下存取的过程,存放过程:

        而取出是从左向右来取,所以取出顺序为22、20、19、19。正序取出。

        我们如果返回-1:

        存放过程:

        取出顺序为19、19、20、22。倒序取出。

        如果返回0:

        发现里面只存进了一个元素:

        现在补充完整这张体系图:

        |——Set:元素是无序(存入和取出的顺序不一定一致),元素不可以重复。(想重复找List,不重复找Set)

                |——HashSet:底层数据结构是哈希表。

                        HashSet是如何保证元素唯一性的呢?

                        是通过元素的两个方法,hashCode和equals来完成。

                        如果元素的HashCode值相同,才会判断equals是否为true。

                        如果元素的hashcode值不同,不会调用equals。

                |——TreeSet:可以对Set集合中的元素进行排序。

                        底层数据结构是二叉树。

                        保证元素唯一性的依据:compareTo方法return 0。

                        TreeSet排序的第一种方式:让元素自身具备比较性。                                          元素需要实现Comparable接口,覆盖compareTo方法。                                                            这种方式也称为元素的自然顺序,或者叫做默认顺序

                        TreeSet的第二种排序方式:                                                                                                          当元素自身不具备比较性时,或者具备的比较性不是所需要的,                                                  这时就需要让集合自身具备比较性。                                                                                              集合初始化时,就有了比较方式。

    04-集合框架(实现Comparator方式排序)

        接下来讲TreeSet的第二种排序方式:

                        TreeSet的第二种排序方式:                                                                                                          当元素自身不具备比较性时,或者具备的比较性不是所需要的,                                                  这时就需要让集合自身具备比较性。                                                                                              集合初始化时,就有了比较方式。

        集合对象以初始化就有的比较方式,就要看一下构造函数了。

        为了让集合自身具备比较性,就定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。

        我们依然在上节课代码的基础上做出修改。

        上节课是按照年龄排序,现在我们的需求变了,想按照姓名来排序。

        我们需要使用的这个构造函数要用到这个东西:

        点进去看看:

        发现是一个接口,这个接口有两个方法:

        它的返回值:

        下面定义一个新的类实现Comparator接口:

        在定义的容器的时候将这个新的类的对象作为参数传进去:

        运行:

        成功了哦。

        接下来解决姓名相同年龄不一样的情况:

        问题解决了呢。

        我们还有更简单的方式来写。年龄是一个整数,而整数有自己的对象:Integer。

        我们去Integer类中看一下,发现它也实现了Comparable接口:

        所以它也有compareTo方法:

        所以我们这样写就可以了:

        运行:

        都是OK的。

        有两种排序方式:让元素自身具备比较性,让容器自身具备比较性。

        当两种排序都存在时,以比较器为主。(比如上面例子中,最后就是以比较器(姓名)为序排序的)

        定义比较器:定义一个类,实现Comparator接口,覆盖compare方法。

        这两种排序方式中,比较器比较常用一些。

        比较器是什么概念呢?

        叫做你具备了比较性,我就按照你的排,如果你没有比较性/或者比较性不是所需要的,我对外提供了一个规则,你只要按照这个规则来写,我依然可以帮你排。

        这个规则就是Comparator。

        接口就是对外提供的功能扩展。

    05-集合框架(TreeSet练习)

        练习:按照字符串长度排序。

        我们先运行一下这段代码:

        发现它是按照自然顺序来排的。

        字符串本身具备比较性,但是它的比较方式不是所需要的,这时就只能使用比较器。

        我们修改一下代码:

        运行:

        发现排序成功。

        我们还可以让代码更简单一点,依然是上节课的那个方法:

        运行:

        发现“aaa”没有存进去。

        因为它和“cba”长度一样。

        我们继续修改代码,主要条件判断完了,要判断次要条件:

        再运行:

        OK了。 

        当然,我们也可以用匿名类的方式来完成:

        但是这样阅读性就差一些,所以还是用正常方式写好一点。

    06-集合框架(泛型概述)

        接下来讲下一个知识点。

        先看示例:

        我们集合当中会添加很多对象,比如说添加一个Integer对象:

        集合当中只能添加对象,是不能添加基本数据类型的,但是在1.5版本之后可以添加基本数据类型了,因为基本数据类型有一个自动装箱拆箱动作,所以我们直接这样写就行,自动把4封装成对象了,两句话意思一样,:

        我们试着编译运行一下,发现编译并没有问题,可是运行的时候就挂掉了:

        这个异常是类型转换异常,说Integer不能转换成String。

        可是编译的时候都没有发现,运行时却挂掉了。我们有没有办法让它在编译时就可以发现问题呢?

        我们分析一下问题怎么产生的:存入了不同类型的数据。

        那么,如果往里面全存成String,是不是就可以了呢?

        Java在1.5版本的时候就对这个问题进行了解决,提供了一个新技术,叫做泛型

        泛型:JDK15版本以后出现的新特性,用于解决安全问题,是一个类型安全机制。

        再复习一下新特性产生的三种原因:1,高效。2,简化书写。3,提高安全性。

        那么它如何解决安全性问题呢?

        我们这里可以借鉴数组定义的原则来完成。

        我们看这个示例:

        编译的时候就出错了:

        为什么呢?

        因为在定义数组容器的时候已经明确类型了,已经确定是int了。

        这也是数组比较安全的原因。

        而集合在定义的时候并未指定元素类型,所以存在了安全隐患。

        如果能像数组一样在集合定义时就指定类型,是不是就没有安全隐患了呢?这就是泛型的由来。

        我们现在为集合指定数据类型:

        这句话的意思是:我定义了一个容器,叫做ArrayList容器,这个容器中的元素是String类型。

        为什么使用的是尖括号<>呢?因为大括号{}已经被程序使用了,小括号()被参数使用了,方括号被[]数组使用了,就只剩尖括号<>了(;′⌒`) 

        好了,现在编译,发现失败了,问题从运行时期就转移到了编译时期:

        好,我们把那条错误语句去掉,然后再做一些小修改,将迭代器也指定数据类型:

        编译,发现错误提示没有了:

        而且,不仅错误提示没有了,之前编译会有的这两句注意事项也没有了:

        为什么没有了呢?

        因为安全了呀。

        泛型的好处:

        1,将运行时期出现的问题ClassCastException,转移到了编译时期,方便于程序员解决问题,让运行时的问题减少,提高了安全性。

        2,避免了强制转换的麻烦。

    07-集合框架(泛型使用)

        泛型格式:通过<>来定义要操作的引用数据类型。

        那么在使用Java提供的对象时,什么时候写泛型呢?

        通常在集合框架中很常见,只要见到<>就要定义泛型。

        我们看一下Collection接口:

        他就有<E>,在这里,E是Element,即元素的意思,它并没有什么具体的含义。

        我们再看一下他的子类ArrayList:

        它里面也有E。

        我们再看看方法列表:

        比如add方法,我们明确的是什么类型的元素,那么add方法添加的就是什么类型的对象。

        其实<>就是用来接收类型的。

        当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。(就如同我们的函数传参数一样)

        接下来写我们依然写之前写过的那个例子,想将存进集合中的字符串按长度来排序。

        我们发现Comparator接口也可以指定数据类型:

        它也可以加泛型,因为泛型避免了强转

        像我们之前写就需要强转:

        而现在为它指定了数据类型,就不用强转了。

        整个方法的代码就非常简单了:

        编译运行:

        执行很顺利,而且不再有安全提示了。

        我们在写HashSet集合的时候,覆盖了两个方法,一个叫做hashCode,一个叫做equals,equals中必须要写Object。因为这个equals复写了Object,Object有泛型吗?没有。所以必须得做转换,转换之前还得要判断它是不是这种类型。

        再总结一下,我们要定义一个对象需要做的事情:里面要自定义hashCode和equals方法,同时还要实现comparable接口,重写compareTo方法,让对象具备默认的比较性,这样既可以存到HashSet当中,又可以存到TreeSet当中。(不一定存储到哪个里面去,所以这些基本的特性都要具备)

    08-集合框架(泛型类)

        接下来有一个问题,我们能不能在定义的类中使用泛型的概念,来完成程序的设计呢?

        换句话说,Java能定义出一堆泛型类,我们能不能定义出呢?

        接下来体现一下泛型出现前后的代码。

        这里有一个对应Worker类的工具类,接下来我们想定义一个Student类,也要给它定义一个工具类。那么,每个类都对应一个工具类,是不是太麻烦了呢?

        如果只是为了设置对象和获取对象,对象类型不确定,都是后面有的。

        这时,我们是不是可以提高一下程序的扩展性,抽取这些对象的共性类型。

        所以换一种定义方式:

        这样是否就通用了呢?

        试一下:

        编译运行发现都没有问题:

        我们将代码修改一下:

        这时再运行就出现了类型转换异常的问题: 

        其实,我们这里所写的Tool类,以前程序设计的时候都用这样的方式来完成程序的扩展,比如最常见的之一equals,它就是Object类型的。

        这就是早期的代码,程序员只能自己弄清楚到底是什么类型,不能用什么类型,是很手工的方法。

        接下来是泛型出现后的代码: 

        这就是传说中的泛型类。

        主函数中:

        编译运行都没有问题:

        把最后一句的强转去掉:

        发现编译运行也是OK的。(图略)

        传进Student对象试一下:

        编译时就会报错,直接让问题发生在了编译时期:

        什么时候定义泛型类?

        当类中要操作的引用数据类型不确定的时候,早期定义Object来完成扩展,现在定义泛型来完成扩展。

        注意:这里是引用数据类型哦,基本数据类型定义不了。

    09-集合框架(泛型方法)

        泛型除了可以定义在类中,也可以定义在方法中。

        例:

        给类定义泛型后,为我们提供了很大方便,可是也出现了局限性,那就是对象一旦建立,类型就确定了。比如在这个例子中,只能打印固定类型的数据。

        泛型类定义的泛型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上。

        那我们需要建立多个对象,指定不同的数据类型。

        这样就太麻烦了。

        如果我们想实现的是,建立一个对象后,可以通过这一个对象打印多种类型数据。

        修改后的代码:

        不同类型的元素就打印出来了:

    10-集合框架(静态方法泛型)

        那我们可以在泛型类中定义泛型方法吗?

        可以的。

        比如这样:

        使用它:

        现在我们定义了一个静态方法:

        编译会出现错误提示:

        因为静态方法在对象没有建立的时候就存在了,而元素类型T只有在对象建立之后才会指定,所以出现问题了。

        特殊之处:

        静态方法不可以访问类上定义的泛型,如果静态方法操作的引用数据类型不确定,可以将泛型定义在方法上。

        OK,我们修改一下这个静态方法:

        再使用它运行就成功了:

        注意不可以写在这里哦:

        会编译失败:

        这叫书写格式错误,泛型定义在方法上时,永远要放在返回值类型的前面,修饰符的后面。

    11-集合框架(泛型接口)

        泛型定义在接口Inter上,InterImpl类在实现这个接口的时候指定了数据类型:

        运行,没有问题:

        接下来是另一种情况,InterImpl类在实现这个接口的时候也不知道该操作什么数据类型(这种用法其实不多见):

        运行,也是OK的:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容