JavaSE进阶(六)——集合框架

本文介绍了Java集合框架中的List接口,重点对比了ArrayList和LinkedList两种实现方式。ArrayList基于动态数组,适合快速查找和修改,但插入删除效率低;LinkedList采用双向链表,插入删除高效但查找慢。此外,文章还提到了集合框架的基本接口和Map接口的重要性。
摘要由CSDN通过智能技术生成

JavaSE进阶(六)——集合框架

前言

很抱歉在网络编程的记录开始后中断更新,转而来更新集合框架,这是我个人的疏忽,集合框架应该在线程之后更新,配合线程的概念将集合中关于线程安全方面的API说明清楚,大家可以先看集合框架后再去阅读网络编程。

概述

集合

我们最早基础集合的概念是在数学中,可能由于求学年代的不同,接触集合的年级也不大一样,暂且举一个例子:我们在初中学习函数的时候会把自变量和因变量的取值范围分别命名为各自的集合(反正我的老师这么教的,我们学的也不错)。这个时候我们知道集合是存放相同类型数据的容器,在JavaSE基础篇中的数组说明的时候,我们说过数组是相同类型数据的集合,其实数组也是一种集合的概念。

但是数组与我们集合框架中的概念有联系又不太一样,比如:我们的数组智能表示一些简单类型的数据,或者我们可以自定义引用类型数组的方式来存放一些比较复杂的数据。但是这种存放方式中各个数据之间的关系独立,它们之间只有数组下标相对应。

在Java篇最开始说过,Java语言是更贴近人类思维的编程语言,在人类思维中,我们会将一些客观存在之间的关系有一个明确的定义。简单来说:你爸是你爸,你是你爸孩子,你们之间的关系被明确定义了。而在计算机中,每个客观存在都被定义为对象,对象与对象之间的关系需要一个明确的定义,否则独立的对象之间没有任何交集,如果这样的对象在我们的程序中没有任何意义,我们会认为这个是垃圾对象,会被虚拟机中的GC线程清理掉。就好比现实生活中,bd在会见外国友人的时候括约肌失守和我们有关系吗?(逗个乐子不算哈)

这些对象之间的关系我们无法靠数组的下标来直接定义,手动定义又太麻烦。所以我们将对象之间所有的关系抽象出来定义好这些关系的规则,然后创建除了对应规则的容器供我们使用。

现在来说一下,对象与对象之间的关系被抽象成为了什么呢?答案就是数据结构。数据结构中一共有四种基本结构:

  • 线性结构:小学生排队进教室;
  • 树型结构:想象一下族谱;
  • 图形结构:百度导航,在重庆使用导航从家去往下一个地点,不考虑时间和距离我们有很多条路,也可以理解为这两个地点之间有多个联系;
  • 集合结构:不可避免的有一些对象之间互相毫无关系,但是他们却又在系统中有不可或缺的地位,我们将这些对象放在一起。

数据结构的概念以后再说,说到这里可以总结一下,集合框架就是数据结构在Java中的具体实现。

链表与数组的区别

这是一道入门级面试题,链表是什么?别在意,最迟第三篇笔记会给大家一个手写链表的笔记和源码,大家先来巩固一个概念。

之前说过数组的创建是在内存中申请一块连续的内存空间来存放(当时没特意说,打算在这里说明一下),什么样的连续空间呢?大家可以联想一下德芙巧克力,一块一块挨在一起。而链表的内存空间是什么样的呢?链表使一种内存空间不连续的存储结构,存储空间不连续注定不能用数组下表这种方式来让元素之间记录对应的位置,所以链表中没个元素位除了存放当前元素的值以外还会预留一部分空间存放下一个元素的位置信息(指针?引用?物理地址编号?无所谓,先记着结构就行)。这就是数组和链表之间的区别,总结一下:

  • 数组元素存储的物理空间连续,链表元素存储的物理空间不连续;
  • 数组元素存放当前元素的值,链表元素中除了存放当前元素的值以外还需要存放下一个元素的位置;
  • 数组元素寻找其他元素通过数组下标,链表元素寻找其他元素通过临近元素的物理空间信息。

这道面试题答到现在还是不合格,因为某(内)种(卷)的原因,我们需要回答更多的特性,剩下的在数据结构篇详细说明,这里不说,嘿嘿。(别怪我,现在写的话相当于再开一个数据结构专栏,字数堪比中篇小说,写不了写不了)。

集合框架

基本接口

我们在集合框架中主要研究三大接口及其衍生品,被淘汰的就不会说明了,未来面试的时候非得追着你问淘汰掉的接口的话,别留情,怼他,然后拉黑!

集合框架三个接口:

  • List;
  • Set;
  • Map。

只说这些可能不理解,上图!

在这里插入图片描述

上面黑色模块的是一种迭代器,迭代器在本篇笔记中不会特意说明,但是在集合框架的笔记中会有详细说明。接下来对图中的说明请忽略Iterator

Collection和Map是两个接口,从图中可以看出二者没有任何关系,他们属于不同的类别。

接下来我举一个当年我的老师给我们讲的例子:

在集合的世界中有两个帮派,一个是克莱克什家族,另一个是迈普家族。克莱克什家族的族长就是克莱克什一世,能力特别强,所以产业发展特别快,自己忙不过来,就选拔了两个二当家,一个叫赛特,另一个叫利斯特。

赛特和利斯特认为老大能力那么强都需要找人帮忙,我们也不可能总是亲力亲为。所以,二人有各自选拔了属于自己的小组长,每个小组长按照能力分配了不同的任务。

说到这,大家可能不太理解这个例子的含义。其实就是单一职责原则,我们尽量按照能力的强弱,扬长避短的分配功能,尽量发挥个体的长处。

先说一下List:

List就是我们经常说的线性结构,它最主要的特性就是有序,其中ArrayList第底层由我们的数组实现,而LinkedList底层由链表实现。说到这大家请看一下前文说过的数组与链表的区别。在此我们接着说明一下二者更深层次的区别:

  • 由于Java默认的数组底层的物理结构无顺序表,顺序表就是物理存储空间连续的结构,我们可以根据数组下标快速的查询到对应的元素。同时,修改和查询的操作相似,只是在找到位置后修改元素即可,所以数组(顺序表)的查找和修改操作速度很快,但是插入和删除就不是很快,如果要在数组中间位置插入和删除,则会出现没有空间或者遗留空间的问题,这样就不符合我们数组(顺序表的物理结构),所以需要将插入或者删除位置以后的所有元素全部移位,效率很低。
  • 链表用可能用C语言的方式说明更好理解,所以下面分别使用Java版和C/C++版的说明:
    • C/C++版:链表的存储空间不连续,每个元素之间都是通过指针来标记,也就意味着我们没有办法通过下标的方式来定位对应的元素(就算我们在代码中使用下标来定位,计算机底层也是从链表的第一个元素一步一步找到相对应的元素位置),所以查找起来效率相当低,同样,修改也是一个道理。但是一旦需要删除或添加操作的时候,链表的优势就显现出来了,我们只需要将相邻的的两个元素的指针形成一个“第三者插足”的结构就可以完成添加的操作,相反的,删除操作只需要将指向被删除的元素的指针跳过该元素就,并析构该元素就可以了。虽然在查找这个元素的时候依旧需要一步一步找下去,但是我们不需要在执行结束后移动元素位置了。
    • Java版:链表在Java中通过引用的方式存储临近元素的物理空间,我们在操作的时候和C/C++语言逻辑相同,只是需要将元素空间中存放的临近元素的地址信息修改即可。
ArrayList(动态数组)

首先说明,该接口底层是由数组组成的,且长度为动态,即长度可变。默认长度为10,如果空间不够的话,我们会自动扩充当前长度的二分之一。

该动态数组是一个有序的集合(序列),此接口对于列表中的每个元素的插入位置进行精确地控制。我们可以通过索引(下标)访问元素,也可以通过搜索列表中的元素。

// 我们使用向上转型的方式来使用这个工具。
// 默认长度为10
List<String> list1 = new ArrayList<String>();
// 同时我们也可以直接传递一个数组长度为参数实例化一个对象;一般不建议使用,因为它本身是动态数组。
List<String> list2 = new ArrayList<String>(20);
相关方法
  • add():向ArrayList对象的尾端加入一个对应类型的元素。
// list1.add(8,"这种添加方式不建议使用");
list1.add("1");
list1.add("2");
list1.add("3");
list1.add("4");
list1.add("2");
  • remove():删除数组元素
// 根据索引删除元素
list1.remove(0);
// 根据元素值删除元素(只会删除第一个)
list1.remove("2");
  • set():修改制定索引的数据
// 将索引为4(第五个元素)的值修改为5
list1.set(4,"5");
  • toArray():将列表转换为数组
String[] strs = new String[list1.size()];
// 将列表转化为字符串数组
strs = list1.toArray(strs);
// 循环输出转化后的数组
for(int i = 0; i < list1.size(); i ++){
	System.out.println(strs[i]);
}
// 迭代输出转化后的数组
for(String str : strs){
    System.out.println(str);
}
// 同时我们也可以直接遍历列表
for(String str : list1){
    System.out.println(str);
}
  • sort():排序
Collection.sort(list1);
LinkedList

这个列表的底层是由链表组成的。

上文中已经说过,链表和数组有很大的区别其实最大的区别还是在名称上。我们在数组那篇笔记中有过说明,数组只是一个概念,这个概念规定了一些基本规则,比如存储空间连续等,其实在数据结构中有一个专有名词来描述着中结构——顺序表,所以非要说区别的话应该说顺序表和链表之间的区别,数组只是在编程语言中将顺序表实现出来而已,如果我们自己使用代码手写一个“数组”的结构,我们也可以使用链表来完成。

言归正传,LinkedList这个链表和普通的链表有所区别,它的结构是双向链表。普通的链表只会知道自己的下一个相邻元素的位置,无法知道自己上一个相邻元素的位置(在C语言中意味着我们没有存储指向上一个元素位置的指针)。而双向链表的结构类似于小学生手牵手排队,除了第一个小朋友和最后一个小朋友外,我们都牵住了离自己最近的小朋友的手。

这样的结构适合插入与删除,上文已经说过原因,在此不做赘述、

该列表在创建的时候默认不会预先定义长度,我们只是创建了第一个元素的位置(头结点),日后在需要添加元素的时候才会生成一个空间来做第二个节点。

至于方法和ArrayList没有区别。

总结

本篇笔记中的知识量众多,同时还将单一职责以及接口隔离原则做了一定的解释。

注意:再次重申,看过我很多笔记的同学获取能够发现,我的笔记中对于技术规则、语法等概念都是核心以及日常使用应该注意到的;关于长篇大论举例子来说明的往往都是思想。所以对于初学者我建议都看。对于只需要技术的同学,个人建议我的笔记不适合你,你应该去看视频教程。

下一篇笔记可能会很枯燥且大篇幅记录代码,因为我要给初学者们手写链表。

原谅我们无法面对面讲解或视频授课,我是真的希望同学们不要走我走过的弯路,现在普通本科的教育质量该提高了,我们从初学者用尽全力学到入门的技术和理论可能已经是淘汰掉的。就像学校让大家使用JDK8版本,却又不讲接口新特性以及Lambda表达式一样,为什么不讲?怕学生听不懂?还是不敢讲?呵,我不知道。

原谅我总在笔记的总结阶段夹带私货,我也希望你们不要相信我,有自己的思考能力,不要谁说什么就是什么。

我不知道我能坚持互联网精神多久,但是我坚持过。

有些人进入社会就跪下了,有些人一生不屈落得凄惨落魄。我没那个勇气面临凄惨的未来,但是我希望未来在我喜欢的藤椅上晒太阳时能回忆起,当初,我站起来过!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值