Java基础day14笔记:集合框架|迭代器|ArrayList|LinkedList|Vector|HashSet

2019/7/1 二刷结束留念

mark:重写equals和hashCode方法的情景~


    01-集合框架(体系概述)

        为什么会出现集合类呢?        

        面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。

        那存到哪里去捏?

        有两种存储的地方可以用,第一种是数组,第二种是集合。

        对象多了用集合存,数据多了用对象存,像姓名年龄这些都是数据~

        可是已经有数组啦,为什么还要有集合呢?

        数组是固定长度的,集合是可变长度的。

        而且,数组当中存储对象的时候,它只能存储同一种类型的对象,比如说全存Demo或者全存Person,因为数组定义的时候已经指定了数据类型啦。

        而集合不是,对它来说只要是对象就行。先存一个new Person()进去,再存一个new Demo()进去,都没问题哒。

        所以,集合的特点是:集合的长度是可变的,集合可以存储不同类型的对象。

        集合作为一种容器,也划分成了很多种。对它们不断的进行共性抽取,就形成了一个体系,我们叫做集合框架。

        嘿嘿,这个图看着理解一下:

        这个体系里面包含很多内容。

        框架产生之后,我们一般会先看什么呢?

        是不是会先看顶层呀~因为顶层当中定义的是这个体系当中最共性、最基本的行为,把这个顶层看明白之后,这个体系的基本功能就了解了。

        看完了顶层之后,找底层来用~

        为什么呢?

        有两点原因:

        1,不断向上抽取出来的东西很有可能是不能创建对象的,它可能是抽象的。

        2,创建子类对象,方法更多一些。

        所以说,参阅顶层,创建底层。

        而这个顶层,就叫Collection,收集、集合的意思。

        集合框架是工具包中的一个成员,它在java.util这个包中。

        Collection是一个接口。在不断向上抽取的过程中,全变成抽象辣~它有很多实现类,也有很多子接口。

        因为子接口太多,就不挨个讲啦。重点讲两个:List和Set。

        把刚刚那个示意图简单填充了一下:

        这些就是我们要重点讲的内容。

        那么问题来啦,为什么要出现这么多种容器呢?

        因为每一个容器对数据的存储方式都有不同。

        这个存储方式称之为:数据结构。

    02-集合框架(共性方法)

        我们先从Collection开始学起~

        Colletion是Colletion层次结构中的跟接口。

        方法摘要:

        在上述方法中,我们发现了一些不清楚的数据类型:

        我们看到的这些不懂的字母,就先把它们理解成Object类~

        顶层方法看完该建立子类对象啦,我们先建立ArrayList对象吧~

        add方法的参数类型是Object,以便于接收任意类型对象。

        集合中存储的都是对象的引用(地址)。就像这样,简单示意图:

         打印集合对象:

        删除元素:

        运行结果:

        写这句就全删啦(这个叫清空集合):

        判断元素: 

        取交集:

        取交集,al1中只会保留和al2中相同的元素~

        再试试removeAll,是将和al2中相同的元素删掉~

    03-集合框架(迭代器)

        在Collection中,有一个方法叫iterator:

        它是一个接口诶:

        这个接口中有三个方法:

        试着用一下:

        Iterator it=al.iterator();//获取迭代器,用于取出集合中的元素。

        我们发现它将集合中的前两个元素拿出来啦。

        但是如果这个集合中元素很多,这样一个一个写就太麻烦了。

        我们有更方便的方法呢:

        那么什么是迭代器呢?

        其实就是集合的取出元素的方式。

        对于取出这个动作,一个函数不能完全描述,需要用多个功能来体现,这种情况下,就需要将功能们封装到一个对象中去。

        画个图解释一下迭代器的特点,这里有三种容器,每个容器中都有一个取出的对象:

        而且,因为数据结构的不同,每个取出对象中的实现方式也不一样。

        那么,这个取出就需要被描述一下,怎么描述呢?

        通过一个类来完成,而这个类,就定义在了集合的内部。

        为什么定义在集合内部呢?

        因为元素就在集合内部,如果想要操作这些元素,是不是集合的内部类最方便啦。

        一般取出分为两步:1,判断元素是否存在2,若存在则取出。

        根据容器数据结构的不同,每个容器判断和取出的具体实现细节不一样。但是它们都是有共性内容:判断和取出,那么可以将共性抽取,形成一个接口,这个接口就是Iterator:

        这些内部类都符合一个规则,该规则就是Iterator。

        如何获取集合的取出对象呢?

        通过一个对外提供的方法:iterator();

        这个对外提供的方法就相当于娃娃机外面的操纵杆,而夹子就相当于迭代器,它是封装在娃娃机里面的,可以取出娃娃,只对外暴露了操纵杆这个方法,可以让人使用。不同的娃娃机,它们的夹子也不一样,有两个钩的,有五个钩的,实现方法都不一样,但它们都是夹子~

        后面我们再定义新的容器,只要将它的取出方法实现Iterator接口就可以啦:

        取出方式就这样被统一啦,再新加集合也不怕~

        除了用while循环,它也可以用for循环写:

        这两种循环有什么不同呢?

        推荐用for循环,因为for循环结束后,it就释放了,不再占用内存空间,而while循环的话则依然占用内存空间。

    04-集合框架(List集合共性方法)

Collection

        |——List:元素是有序的,元素可以重复,因为该集合体系有索引。

        |——Set:元素是无序,元素不可以重复,该集合当中没有索引。

        我们先讲List。

        我们去List类中看一看它的方法~共性方法就不说啦,它的特有方法有:

        在指定位置插入指定元素:

        通过索引获取元素:

        获取元素的索引:

        List集合特有的迭代器:

        移除指定元素:

        改变指定位置的元素:

        获取指定区间元素(包含头不包含尾):

        综上,凡是可以操作角标的方法都是List体系特有的方法。

        总结一下:

        接下来挨个演示一遍。

        在指定位置添加元素:

        删除指定位置的元素:

        修改元素:

        通过角标获取元素:

        获取所有元素:

        如上,List集合有自己特殊的取值方式,就是遍历。

        我们用迭代器也可以实现这个功能:

    05-集合框架(ListIterator)

        通过indexOf获取对象的位置、获取指定区间的元素:         

        接下来看一下列表迭代器listIterator。

        它和普通迭代器到底有什么不同呢?

        看示例: 

        程序运行过程中挂掉了:

        我们来看一下这个异常:

        我们来分析一下:

        所以就存在安全隐患啦。不能同时用多种方式操作同一种元素,否则可能会发生并发修改异常。

        那该怎么解决呢?

        要么全用集合的方法,要么全用迭代器的方法。

        全用迭代器的方法:

        java02不是被删除了吗?为什么篮框部分还被打印了呢?

        因为虽然这个元素的引用被移除了,可是在移除前它还被obj使用了,后面打印的是obj~

        注意,刚刚用的都是迭代器的操作,做了判断、取出和移除,迭代器只能做这三个动作,添加和修改动作它做不了,有局限性。

        不要怕~

        它也知道自己有局限性,所以安排了一个小弟:

        列表迭代器ListIterator有指针,有角标,所以它的方法比它的爸爸Iterator多多啦:

        增删查改样样都可以呢。

        List集合特有的迭代器:ListIterator是Iterator的子接口。

        我们用一下~ 

        添加:

        修改:

        除了hasNext,它还有hasPrevious:

        试一下~ 

        我们反向取一次:

        实际开发中逆向遍历用的少一些,正向遍历用的多一些。

        ListIterator出现后,可以对集合进行在遍历过程中的增删改查。

    06-集合框架(List集合具体对象的特点)

        Colletion中List集合的共性方法说完以后,介绍一下List集合中常见的三个子类对象: ArrayList、LinkedList、Vetor。

        这三个子类对象的出现,是因为底层的数据结构不一样而出现的,那么它们的底层到底是什么样的数据结构呢?换句话说,它们的底层到底是怎样对数据进行存储的呢?

        ArrayList:底层的数据结构使用的是数组结构。特点:查询速度快。但是增删稍慢(元素不多还好,多了之后慢得就比较明显)。线程不同步。

数组数据结构

        LinkedList:底层使用的链表数据结构。特点:增删速度很快,查询稍慢。


链表数据结构

        Vector:底层是数组数据结构。线程同步。(它1.0就出现了,那个时候还木有集合框架呢,而ArrayList是1.2出现的)被ArrayList替代了。

        ArrayList和Vector中,建议用ArrayList,因为效率高,无论增删还是查询,Vector都稍慢。

        那多线程怎么办呐?

        可以自己加锁~

        那我自己加锁不安全怎么办呢?

        就怕你不会加所以Java的集合框架中提供了另外一个功能,专门帮助小笨笨来加锁的~

        怎么加呢?“把你的不安全的给我,我给你个安全的。”好霸道总裁。。。

        还有一个小问题~ArrayList和Vector都是数组数据结构,而数组数据结构的特点是它的长度是固定的,集合却是可变长度的,所以ArrayList和Vector是可变长度的数组。什么叫可变长度的数组呢?

        ArrayList默认的长度是10:

        当长度超过之后,它会new一个新的数组,长度是多少呢?50%延长,变15。然后,把原来数组中的元素copy到新数组中来,再把新元素拼接到它们后面。

        而Vector长度也是10,超过之后new的新数组是100%延长。

        两者相比,ArrayList好一些,因为它既可以延长又可以节省空间。Vector就比较浪费空间啦。

        总的来说Vector现在都不用了,像这张图中都没有Vector:

        最常用的就是加粗的这四块。

    07-集合框架(Vector中的枚举)

        接下来讲一下Vector的特点。(不能理解既然它都不用了还要讲它的特点是为什么。。。)

        Collection通用的方法:

        Vector特有的方法(一般带Element的都是它的特有方法):

        添加元素:

        查找元素:

        插入:

        删除:

        还有其他的:

        我们来看一下这个方法:

        这是什么东西鸭,木有见过呢:

        点进去这个接口看一下(这个接口叫枚举):

        那这个接口都有什么方法呢:

        话不多说,先玩一下~

        枚举就是Vector特有的取出方式。

        发现枚举和迭代器很像。

        其实,枚举和迭代是一样的。

        因为枚举的名称以及方法的名称都过长,所以被迭代器取代了,枚举郁郁而终了。

        但是IO当中有一个对象用到了枚举,因为那个对象也是1.0粗现的,那个时候没有迭代,只有枚举。

        Vector和ArrayList的区别是什么?

        Vector支持枚举,ArrayList没有枚举。

    08-集合框架(LinkedList)

        我们来看一看LinkedList里面有什么特有方法~

        试一下,addFirst:

        再一次,addLast和getFirst、getLast:

        removeFirst:

        get...()和remove...()方法的区别:get可以获取元素但不删除元素,remove获取元素,但是元素被删除。

        想把里面元素全取出来~

        正着取:

        倒着取:

        removeFirst方法会抛出异常:

        到了后期,LinkedList方法升级了,它里面加了一个新的方法,pollFirst/Last:

        如果列表为空它不再抛出异常,而是返回null,这是1.6版本出现的,以后推荐用这个方法喔。

        获取但不移除元素的新方法:

        插入元素的新方法:

        它们替代了之前旧的方法。

    09-集合框架(LinkedList练习)

        练习:使用LinkedList模拟一个堆栈或者队列数据结构。

        描述队列:  

        封装好之后,我们就可以直接使用啦:

        注意哦,像这样的封装很常见。

        为什么要封装呢?我们直接用LinkedList不就可以完成了吗?

        但是LinkedList只有自身的含义,叫做链表,我们想把它做成跟我们项目相关的一些容器,那么我们需要起一些特定的名称来用更方便一些,这个时候我们就会将原有的集合封装进我们的描述当中,并且对外提供一个自己更容易识别的方法名称。

        好啦,存完之后该取啦:

    10-集合框架(ArrayList练习)

        练习:去除ArrayList中的重复元素。

        思路:创建一个新容器,将旧容器的东西一个个放进去,放的时候判断它是否已存在在新容器~

        代码: 

        接下来删除重复的:

    11-集合框架(ArrayList练习2)

        练习:将自定义对象作为元素存储到ArrayList集合总,并去除重复元素。

                   比如:存人对象。将同姓名同年龄,视为同一个人,为重复元素。

        思路:

        Person类:

        主函数中:

        可是为什么这样写会出错呢?

        因为在往容器里面存Person的时候,add的其实是Object,al.add(Object obj),因为只有它能接收任意对象。把Person传进去,其实相当于Object obj=new Person("lisi01",30);,这时Person就被提升为Object了。而后面的it.next()往回返的时候,返回的是Object,Object中并没有getName方法,多态编译失败。

        应该改成这样:

        两句合一句:

        下面一句话也改一下:

        运行:

        搞定。

        接下来去重。

        将之前写好的singleElement方法直接拿过来用:

        我们可以看到,contains方法中其实调用了equals方法:

        而默认的equals方法显然不适用于判断我们新建立的对象,所以我们要在Person类中重写equals方法:

        调用singleElement方法:

        删除成功:

       List集合判断元素是否相同,依据的是元素的equals方法。 (其他的集合肯定不一样)

        虽然主函数中没有直接调用equals方法,但是因为contains方法调用了equals方法,而singleElement方法又调用了contains方法。

        这个问题搞明白之后,我们还会明白一系列其他问题。

        先将重写的equals方法注释掉,我们现在删除一下其中一个元素:

        但运行结果是false而且删除失败了:

        为什么呢?

        因为要删除这个元素,就得现在容器中寻找这个元素。remove底层也调用了equals。而默认的equals方法是对比对象是否是同一个,new的Person是新的对象,地址值不相同,所以肯定会删除失败。

        现在将重写后的equals方法去掉注释,再试一下:

        成功啦。

    12-集合框架(HashSet)

        讲完了List,那么ArrayList和LinkedList该用哪一个呢?

        如果元素中涉及了频繁的增删操作,用LinkedList。

        如果增删操作不频繁,可以选择用LinkList,也可以选择用ArrayList。

        如果涉及了增删,同时又涉及了查询,建议使用ArrayList。因为其实频繁的增删操作并不多见,一般的情况下都是查询比较多。所以ArrayList作为一个最常见、最大频率使用的容器存在,当实在不知道该用谁,就用ArrayList。

        List派系讲完啦,接下来讲Set派系。

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

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

                |——TreeSet:

        看一下Set接口的方法,发现Set集合的功能和Colletion是一致的。Colletion的方法我们之前都讲过啦,所以就不重复讲啦。

        每个对象都有哈希值,那什么是哈希表呢?

        示例:

         发现它们都有哈希值:

        接下来讲讲什么是哈希表:

        很简单,存放哈希值的表就叫哈希表。哈希表的顺序和元素的存放顺序无关,和哈希值的大小有关。而取元素的时候也是按哈希值来取。

        那会不会有哈希值相同的情况呢?

        若哈希值相同,会判断哈希值所指的对象是否相同,若不同,会在这个哈希值上再向下顺延一个,表中有两个这个哈希值(不知道我理解的对不对,如果相同,那该怎么找对应的对象呢):

        验证无序:

        验证唯一性:

    13-集合框架(HashSet存储自定义对象)

        还和11中的例子一样,存储Person到HashSet中。     

        将那个例子中的代码都复制了过来,主函数中这样写:   

        我们再试试存入重复的Person对象,发现成功了:

        为什么即使重写了equals方法,还是会出现重复呢?

        因为现在往里面存的四个对象都是独立的,都有独立的哈希值,都存入了哈希表。

        而我们现在要做的,就是覆盖Person的哈希值方法,建立Person对象自己的哈希值。

        怎么建立哈希值呢?

        你的判断条件是什么,我就依据你的条件来建立哈希值。比如这里是按照姓名和年龄来判断对象是否重复的,那我们就按照姓名和年龄来建立哈希值。

        代码:

        注意,只有当哈希值相同时,才会调用equals方法对比内容是否相同。

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

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

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

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

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

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

                |——TreeSet:

        所以注意,在开发中,只要描述的对象要往哈希集合中存时,一般都会复写hashCode和equals。    

        还有注意,为了保证哈希值的唯一性,一般会给这里乘个数字(两个对象万一两部分加起来正好是同一个值就重复了):

    14-集合框架(HashSet判断和删除的依据)

        注意,HashSet对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashCode和equals方法,而且先依赖hashCode,再依赖equals。

        ArrayList判断元素是否存在,以及删除等操作,只依赖equals。

        这个原因全都在数据结构上,数据结构不同,它依赖的方法也不一样。    

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

推荐阅读更多精彩内容