目录
有意者可加
作者:研J小政
课堂:wclass
(有什么弊端请私信我,目前参考众多资料精华整理过程过中)
章节:集合知识粗略了解!
QQ:3544839383
资料学习群:
进度:持续更新迭代!
录课状态:待录
参考文献
一、集合
1. 出现的背景
在没有集合类之前,实际上在Java语言里已经有一种方法可以存储对象,那就是数组。数组不仅可以
存放基本数据类型也可以容纳属于同一种类型的对象。数组的操作是高效率的,但也有缺点。比如数组的
长度是不可以变的,数组只能存放同一种类型的对象(或者说对象的引用)
另外,在程序设计过程中,程序员肯定会经常构建一些特殊的数据结构以正确的描述或者表达现实情况。比如描述火车进站出站,他们会用到“栈”这个数据结构,常用的数据结构还有:队列、链接表、树
和散列表等等。这些数据结构几乎在每一段程序设计过程中都会使用到,但是如果每次编程都要重新构建
这些数据结构显然违背了软件组件化的思想。因此Java的设计者考虑把这些通用的数据结构做成API
供程序员调用。
基于以上几点必须解决的问题。Java提供了对象的数种保存方式,除了内置的数组以外,其余的称为集
合类。为了使程序方便地存储和操纵数目不固定的一组数据,JDK中提供了Java集合类,所有Java集合类都
位于Java.util包中,与Java数组不同,Java集合不能存放基本数据类型数据,而只能存放对象的引用。
2. 带大家具体了解下集合
在 Java 后端这一块来了解,集合可以看作是一种容器,用来存储对象信息。
所有集合类都位于java.util包下,但支持多线程的集合类位于java.util.concurrent包下。
Java集合类主要由两个根接口Collection和Map派生出来的,Collection派生出了三个子接口:
List、Set、Queue,因此Java集合大致也可分成List、Set、Queue、Map四种接口体系,
(注意:Map不是Collection的子接口)。
- List代表了有序可重复集合,可直接根据元素的索引来访问;
- Set代表无序不可重复集合,只能根据元素本身来访问;
- Queue是队列集合;
- Map代表的是存储key-value对的集合,可根据元素的key来访问value。
常见的实现类分别是:
ArrayList、LinkedList、ArrayQueue、HashSet、TreeSet、HashMap、TreeMap等。
3. 集合带来了哪些好处
- 降低编程难度:在编程中会经常需要链表、向量等集合类,如果自己动手写代码实现这些类,需要花费较多的时间和精力。调用Java中提供的这些接口和类,可以很容易的处理数据。
- 提升程序的运行速度和质量:Java提供的集合类具有较高的质量,运行时速度也较快。使用这些集合类提供的数据结构,程序员可以从“重复造轮子”中解脱出来,将精力专注于提升程序的质量和性能。
- 无需再学习新的APl:借助泛型,只要了解了这些类的使用方法,就可以将它们应用到很多数据类型中。如果知道了LinkedList的使用方法,也会知道LinkedList怎么用,则无需为每一种数据类型学习不同的API。
- 增加代码重用性:也是借助泛型,就算对集合类中的元素类型进行了修改,集合类相关的代码也几乎不用修改。
4. 集合的特点
- 集合类这种框架是高性能的。对基本类集(动态数组,链接表,树和散列表)的实现是高效率的。
一般人很少去改动这些已经很成熟并且高效的APl; - 集合类允许不同类型的集合以相同的方式和高度互操作方式工作;
- 集合类容易扩展和修改,程序员可以很容易地稍加改造就能满足自己的数据结构需求。
- 可以动态保存任意多个对象,使用方便。
5. 集合和数组对比
- 数组声明了它容纳的元素的类型,而集合不声明。
- 数组是静态的,一个数组实例具有固定的大小,一旦创建了就无法改变容量了而集合是可以动态扩展容量,可以根据需要动态改变大小,集合提供更多的成员方法,能满足更多的需求。
- 数组不论是效率还是类型检查都是最好的。
-
- 数组是大小固定的,一旦创建无法扩容;集合大小不固定,
- 数组的存放的类型只能是一种,集合存放的类型可以不是一种(不加泛型时添加的类型是Object);
- 数组是java语言中内置的数据类型,是线性排列的,执行效率或者类型检查(不懂),都是最快的.
简单来说:
- 数组定义后类型确定, 长度固定
- 集合类型可以不固定, 大小可变
- 数组可以存储基本类型和引用类型的数据, 但是集合只能存储引用类型的数据
6. 数组和集合应用场景(对比)
数组和集合是编程中常用的数据结构,它们可以存储一组数据,并且可以方便地对这些数据进行操作。
在实际的编程中,数组和集合有着不同的应用场景,下面将分别介绍它们的应用场景。
6.1 数组的应用场景
数组是一种线性数据结构,它可以存储一组相同类型的数据。
数组的应用场景非常广泛,下面将介绍几个常见的应用场景。
1. 存储一组数据
数组最常见的应用场景就是存储一组数据。
例如,我们可以使用数组来存储一组学生的成绩,一组员工的工资等等。
在这些场景中,数组可以方便地存储和访问数据,而且可以进行各种操作,例如排序、查找等等。
2. 图像处理
在图像处理中,数组也有着重要的应用。
图像可以看作是一个二维数组,每个元素代表一个像素点的颜色。
我们可以使用数组来存储图像数据,并对图像进行各种操作,例如旋转、缩放、裁剪等等。
3. 矩阵运算
在数学中,矩阵是一个非常重要的概念。
矩阵可以看作是一个二维数组,它可以表示线性方程组、向量空间等等。
在计算机科学中,矩阵也有着广泛的应用,例如图像处理、机器学习等等。
我们可以使用数组来存储矩阵数据,并对矩阵进行各种运算,例如加法、乘法等等。
4. 缓存
在计算机科学中,缓存是一种常见的优化技术。
缓存可以将一些经常使用的数据存储在内存中,以提高程序的性能。
在缓存中,数组也有着重要的应用。
我们可以使用数组来存储缓存数据,并对缓存进行各种操作,例如添加、删除、查找等等。
6.2 集合的应用场景
集合是一种非线性数据结构,它可以存储一组不同类型的数据。
集合的应用场景也非常广泛,下面将介绍几个常见的应用场景。
1. 数据去重
在实际的编程中,我们经常需要对一组数据进行去重操作。
例如,我们需要从一组数据中找出不重复的元素,或者需要统计一组数据中不同元素的个数。
在这些场景中,集合可以方便地去重,并且可以进行各种操作,例如交集、并集等等。
2. 数据排序
在实际的编程中,我们经常需要对一组数据进行排序操作。
例如,我们需要将一组学生的成绩按照从高到低的顺序排列,或者需要将一组员工的工资按照从低到高的
顺序排列。
在这些场景中,集合可以方便地进行排序,并且可以进行各种操作,例如反转、截取等等。
3. 数据分组
在实际的编程中,我们经常需要对一组数据进行分组操作。
例如,我们需要将一组学生按照年龄分成不同的组,或者需要将一组员工按照部门分成不同的组。
在这些场景中,集合可以方便地进行分组,并且可以进行各种操作,例如合并、拆分等等。
4. 数据统计
在实际的编程中,我们经常需要对一组数据进行统计操作。例如,我们需要统计一组学生的平均成绩、最
高成绩、最低成绩等等,
或者需要统计一组员工的平均工资、最高工资、最低工资等等。
在这些场景中,集合可以方便地进行统计,并且可以进行各种操作,例如求和、平均值等等。
7. 知识小结
7.1 集合简介
集合和数组一样都是容器,数组定义完成并启动后, 类型确定、长度固定,集合是Java中存储对象数据的一
种容器,因为集合只支持引用类型, 不支持基本数据类型。
如果需要存储基本数据类型, 需要存储基本数据类型对应包装类!
Java 最初的版本只为最常用的数据结构提供了很少的一组类:
Vector、Stack、Hashtable、BitSet与Enumeration 接口,
其中 Enumeration 接口提供了一种用于访问任意容器中各个元素的抽象机制。
这是一种很明智的选择,要想建立一个全面的集合类库需要大量的时间和高超的技能。
随着Java 1.2 的问世,设计人员感到是推出一组功能完善的数据结构的时候了。
7.2 为什么出现集合
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,
集合就是存储对象最常用的一种方式(java的集合都在 java.util包下 )
数组和集合类都是容器,有何不同:
- 数组虽然也可以存储对象,但长度是固定的;集合长度可变。
- 数组中可以存储基本数据类型,但集合只能存储对象。
集合类特点:
- 集合只用于存储对象,集合长度可变,集合可存储不同类型的对象。
7.3 结论
数组和集合是编程中常用的数据结构,它们有着不同的应用场景。
数组适用于存储一组相同类型的数据,例如学生的成绩、员工的工资等等。
集合适用于存储一组不同类型的数据,例如学生的姓名、年龄、成绩等等。
在实际的编程中,我们需要根据具体的需求选择合适的数据结构,并且合理地使用它们,以提高程序的性
能和可维护性。
二、集合框架(Java)
1. 背景介绍
Java 集合框架 Java Collection Framework ,又被称为容器 container ,是定义在 java.util 包下的
一组接口 interfaces 和其实现类 classes 其主要表现为将多个元素 element 置于一个单元中,用于对这些
元素进行快速、便捷的存储 store 、检索retrieve 、管理manipulate ,即平时我们俗称的增删查改
CRUD 。
例如,一副扑克牌(一组牌的集合)、一个邮箱(一组邮件的集合)、一个通讯录(一组姓名和电话的映射
关系)等等。
2. 集合框架类和接口总览
3. 集合类常见接口及实现类
3.1 Collection接口
Collection接口是最基本的集合接口,它不提供直接的实现,
Java SDK提供的类都是继承自Collection的“子接口”如List和Set
在Java中所有实现了Collection接口的类都必须提供两套标准的构造函数,
- 一个是无参,用于创建一个空的Collection,
- 一个是带有Collection参数的有参构造函数,用于创建一个新的Collection,
这个新的Collection与传入进来的Collection具备相同的元素
2.2 List接口
List接口为Collection直接接口。
List所代表的是有序的Collection,即它用某种特定的插入顺序来维护元素顺序。
用户可以对列表中每个元素的插入位置进行精确地控制,同时可以根据元素的整数索引
(在列表中的位置)访问元素,并搜索列表中的元素。
实现List接口的集合主要有:ArrayList、LinkedList、Stack等。
2.3 Map接口
Map与List、Set接口不同,它是由一系列键值对组成的集合,提供了key到Value的映射。
同时它也没有继承Collection。
在Map中它保证了key与value之间的一一对应关系。
也就是说一个key对应一个value,所以它不能存在相同的key值,当然value值可以
相同。 实现map的有:HashMap、TreeMap、HashTable。
3. ArraysList 和 LinkedList
- ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
- 对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针
- 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。
但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList.
因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。
- ArrayList的大小默认为10,每次扩容为1.5倍,即为
int newCapacity = oldCapacity + (oldCapacity >> 1)。
随着容器中的元素不断增加,容器的大小也会随着增加。
在每次向容器中增加元素的同时都会进行容量检查,当快溢出时,就会进行扩容操作。
所以如果我们明确所插入元素的多少,最好指定一个初始容量值,避免过多的进行扩容操作而浪费时
间、效率。
- ArrayList中的size、isEmpty、get、set、iterator 和 listIterator 操作都以固定时间运行。
add 操作以分摊的固定时间运行,也就是说,添加 n 个元素需要 O(n) 时间
(由于要考虑到扩容,所以这不只是添加元素会带来分摊固定时间开销那样简单)。
- 同样实现List接口的LinkedList与ArrayList不同,LinkedList是一个双向链表。
所以它除了有ArrayList的基本操作方法外还额外提供了get,remove,insert方法在LinkedList的首部
或尾部。
由于实现的方式不同,LinkedList不能随机访问,它所有的操作都是要按照双重链表的需要执行。
在列表中索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。
这样做的好处就是可以通过较低的代价在List中进行插入和删除操作。
4. HashMap 和 TreeMap
4.1 HashMap
以哈希表数据结构实现,查找对象时通过哈希函数计算其位置,它是为快速查询而设计的,其内部定
义了一个hash表数组(Entry[] table),元素会通过哈希转换函数将元素的哈希地址转换成数组中存放的
索引,如果有冲突,则使用散列链表的形式将所有相同哈希地址的元素串起来,可能通过查看
HashMap.Entry的源码它是一个单链表结构。
其不是同步的,且允许空值作为键和值。
HashMap的数据结构为数组+链表/红黑树(冲突的链表多于8个时,就把这个链表转换成红黑树),
初始容量是16,默认加载因子为0.75。
HashMap的长度n为什么是2的整数次幂?
- 一是为了减少hash冲突的概率,n为偶数 最低位为0 异或后结果必为偶数;
- 二是为了加快hash计算,&比%速度快;
- 三是在JDK1.8中,扩容更方便,关注特殊位\
(n-1会多出一位,然后关注和这位对齐的hash,0不变,1加一个扩容之前的n)
HashMap在JDK 1.7中使用的是头插法,在并发put时出现Entry链表形成环形数据结构,导致死循
环,为了解决这个问题,在JDK1.8中使用的是尾插法。
如何保证HashMap的线程安全?
- 一是Collections工具类中synchronizedCollection;
- 二是采用装饰者模式实现map接口对HashMap进行封装;
- 三是直接使用ConcurrentHashMap。
4.2 TreeMap
TreeMap存储K-V键值对,通过红黑树(R-B tree)实现;
TreeMap继承了NavigableMap接口,NavigableMap接口继承了SortedMap接口,可支持一系列的导航
定位以及导航操作的方法,
当然只是提供了接口,需要TreeMap自己去实现;
TreeMap实现了Cloneable接口,可被克隆,实现了Serializable接口,可序列化;
TreeMap因为是通过红黑树实现,红黑树结构天然支持排序,默认情况下通过Key值的自然顺序进行排序;
5. 对集合的选择
5.1 对List的选择
对于随机查询与迭代遍历操作,数组比所有的容器都要快。
所以在随机访问中一般使用ArrayList
LinkedList使用双向链表对元素的增加和删除提供了非常好的支持,
而ArrayList执行增加和删除元素需要进行元素位移。
对于Vector而已,我们一般都是避免使用。
将ArrayList当做首选,毕竟对于集合元素而已我们都是进行遍历,只有当程序的性能因为List的频繁插入和
删除而降低时,再考虑LinkedList。
5.2 对Set的选择
HashSet由于使用HashCode实现,所以在某种程度上来说它的性能永远比TreeSet要好,尤其是进行增加
和查找操作。
虽然TreeSet没有HashSet性能好,但是由于它可以维持元素的排序,所以它还是存在用武之地的。
5.3 对Map的选择
HashMap与HashSet同样,支持快速查询。
虽然HashTable速度的速度也不慢,但是在HashMap面前还是稍微慢了些,所以HashMap在查询方
面可以取代HashTable。
由于TreeMap需要维持内部元素的顺序,所以它通常要比HashMap和HashTable慢。