本文将为大家详细讲解Java中的Collection体系,这是我们进行开发时经常用到的知识点,也是大家在学习Java中很重要的一个知识点,更是我们在面试时有可能会问到的问题。
文章较长,干货满满,建议大家收藏慢慢学习。文末有本文重点总结,主页有全系列文章分享。技术类问题,欢迎大家和我们一起交流讨论!
前言
截止到今天,我们《从零开始学Java系列》的文章已经要到一个新的阶段了。在此之前,我们学习了Java里的各种基础知识,包括环境配置、基本语法、分支循环、数组、常用类等。而从今天开始,本系列文章要给大家介绍另一个我们开发时特别常用的知识点–集合!
其实我们之前学习的数组也是集合的一种,但数组的结构比较简单,并且自身也存在一些天生的缺陷,比如数组的长度是不可修改的。然而在很多情况下,我们在开发时并不能直接确定数据的数量,这就导致我们在开发时不能频繁地使用数组。 所以就需要有一种新的存储数据的结构出现,这就是集合类存在的意义。接下来我们就先从整体上来了解一下集合是怎么回事,希望大家能够通过最近的几篇文章,熟练掌握集合的使用和原理。
全文大约 【4400】字,不说废话,只讲可以让你学到技术、明白原理的纯干货!本文带有丰富的案例及配图,让你更好地理解和运用文中的技术概念,并可以给你带来具有足够启迪的思考…
一. 集合简介
1. 数组缺陷
只要是内部能够持有若干个其他Java对象,并对外提供了访问接口的Java对象,都属于集合。
根据之前我们对数组的认知,数组其实也是一种集合,也可以将不同类型的数据存储起来。既然如此,我们为什么还要搞出来一种新的存储结构来进行数据的存储呢?这主要是因为数组自身存在一些缺陷。
我们虽然可以使用数组来保存多个对象,但数组的长度不可变!也就是说,一旦我们在初始化数组时指定了数组的长度,这个数组的长度就不可变了。如果我们要保存数量不断变化的数据,使用数组的效率就会很差。
另外数组也不能保存具有映射关系的数据。有时候我们想根据某个关键字查找到对应的值,数组是做不到的。比如我们想存储学生的成绩信息,”语文—89“,”数学—60“,数组就很难表达出这种一对一的映射关系。
正因如此,数组难以满足我们在开发时的一些需求,所以这就需要有新的存储结构出现,那么集合也就应运而生了。
2. 集合概念
我们可以把集合理解成是一种用于存放对象的容器,可以保存数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组)。
它就好比是一个锅、一个碗,里面可以存储适当的数据元素,所以集合就是“由若干个确定的数据元素所构成的整体”,负责保存、盛装其他数据,故也被称为容器类。集合的概念在很多编程语言中都有,并不是Java独有的技术。
Java中的集合其实可以理解成是一个”统称“,它实际上主要包括了两种类型的容器,一种是用于存储单个数据元素的Collection集合,属于单列集合,另一种是可以存储键/值对映射关系的Map集合,属于双列集合。
这两个集合接口都是集合的根接口,它们各自又包含了一些子接口或实现类。其中Collection集合又包括3种子类型:List、Set和Queue,而Map集合又包括AbstractMap、SortedMap和HashTable等,如下图所示:
Java所有的集合类都位于java.util包下,提供了一个表示和操作对象集合的统一构架,内部包含了大量集合接口,以及这些接口的实现类和操作它们的算法。
● 接口:代表了集合的抽象数据类型,例如Collection、List、Set、Map等。集合中定义多个接口,是为了能够以不同的方式操作集合对象;
● 实现类:代表了集合接口的具体实现,例如ArrayList、LinkedList、HashSet、HashMap等。本质上它们都是可重复使用的数据结构;
● 算法:代表了实现集合接口的对象在执行的一些计算功能,例如搜索和排序等。这些算法都是多态的表现,因为相同的方法可以在相似的接口上有着不同的实现。
另外集合类和数组不一样,数组里的元素既可以是基本类型的值,也可以是对象(实际保存的是对象的引用变量),而集合里只能保存对象(实际上是保存的对象引用变量,但通常习惯上我们认为集合里保存的是对象)。
3. 集合特点
从集合的设计实现来看,Java集合中的接口和实现类进行了分离,比如有序表的接口是List,而具体实现类则是ArrayList、LinkedList等。另外集合还支持泛型,使得一个集合只能存放同一种数据类型的元素。
集合包括List、Set、Map等几种不同的实现形式,从功能上来看,它们也有各自的特点:
● List集合: List集合是一个有序的、可重复的集合,各个元素对象之间有指定的顺序,且允许出现重复元素和多个值为null的元素对象。
● Set集合: Set集合是一个不可重复的集合,各个元素对象之间没有指定的顺序,元素不允许出现重复值,且只允许有一个值为null的元素对象。
● Map集合: Map集合是一个无序的,具有唯一key键,但值不唯一的集合,元素允许出现重复值,且元素可以根据索引进行查找。
4. 集合分类
根据上面的描述和集合架构图,我们知道,Java中的集合其实可以分为两大类,若干子类:
● Collection: 属于单列集合,内部包括List、Set和Queue等子类,其中List是一种有序列表的集合,Set是可以保证没有重复元素的集合。
● Map: 属于双列集合,是可以通过键值(key-value)查找的映射表集合,内部包括AbstractMap、SortedMap和HashTable等子类。
5. 核心接口
在上面的描述中,给大家说过,组成集合的架构包括了各种接口,这些常用的接口作用如下:
接口名称 | 作用 |
---|---|
Iterator接口 | Iterator迭代器是集合类的输出接口,主要用于遍历输出(即迭代访问)Collection集合中的元素。迭代器是集合接口的父接口,子类实现Collection接口时也必须实现Iterator接口。 |
Collection接口 | Collection是List、Set和Queue的父接口,是存放一组单值的最大接口。所谓的单值,是指集合中的每个元素都是一个对象,但我们一般很少直接使用此接口进行直接操作。 |
Queue接口 | Queue是Java提供的实现队列,有点类似于List。 |
Dueue接口 | Dueue是Queue的子接口,是一个双向队列。 |
List接口 | List是最常用的集合接口。List是有序的集合,允许有相同的元素。我们使用List能够精确地控制每个元素插入的位置,用户能够使用索引(即元素在List中的位置,类似于数组下标)来访问List中的元素,这与数组类似。 |
Set接口 | Set中不能包含重复的元素。 |
Map接口 | Map是存放键值对的接口,该接口中的每个元素都是成对出现,以key-value的形式保存。 |
6. 常用实现类
我们在开发时常用List、Set和Map这3种集合接口,而常用的集合实现类则是ArrayList、LinkedList、HashSet、TreeSet、HashMap、TreeMap等。
类名称 | 作用 |
---|---|
ArrayList | ArrayList是基于可变数组实现的List,优点是可以进行快速地随机访问,查询效率高,但数据的增删改操作速度较慢。 |
LinkedList | LinkedList是基于链表实现的List,对顺序访问进行了优化,数据的增删改操作效率较高,但随机访问的速度相对较慢。 |
HashSet | HashSet是基于HashMap实现的Set,底层使用HashMap来保存所有的数据元素,内部实现比较简单,优化了査询速度。 |
TreeSet | TreeSet是一个有序的Set子类,我们可以从中提取一个有序序列。 |
HashMap | HashMap是根据哈希算法来进行键值对存取的Map集合,属于双列集合。 |
TreeMap | TreeMap是可以对键对象进行排序的Map集合。 |
7. 过时API
集合作为Java里特别常用和重要的API,可以说几乎每个Java项目都有集合的身影。但是集合结构从JDK 1.0开始就被设计出来,其设计实现非常久远,中间经历了几次大的修改。这就像一个庞大的机器经历了几十年的修修补补,虽然性能依然强悍,但也存在一些不适合继续使用的过时API,比如:
● Vector: 一种线程安全的List实现类,不推荐使用;
● Stack: 基于Vector实现的LIFO的栈,不推荐使用;
● Hashtable: 一种线程安全的Map实现,不推荐使用;
● Enumeration: 已被Iterator取代,不推荐使用。
以上这些API都已被遗弃,开发时请大家尽量不要再使用。另外现在我们只是简单地认识一下集合中的接口和个别实现类,后面再详细地给大家进行进行介绍,先不要着急哦。
二. Collection接口
在上面的内容中,说过,集合主要有两大接口,即Collection和Map
,而Collection接口其实又是List和Set的父接口。在开始详细学习List和Set集合类之前,我们先来搞清楚Collection是怎么回事。
1. 简介
Collection接口是List、Set和Queue的父接口,在开发时我们不会直接使用该接口,而是会使用该接口的某个具体实现子类。Collection接口里定义了List/Set/Queue的一些通用方法,这些方法可以操作List、Set和Queue集合,实现对集合基本的添加、删除、判断等操作。
2. 方法
下面是Collection接口中定义的一些常用方法,我们可以先来了解一下这些常用方法的含义,以后开发时这些方法都很常用。
方法名称 | 说明 |
---|---|
boolean add(E e) | 向集合中添加一个元素,添加成功后返回true,E是指数据元素的数据类型。 |
boolean addAll(Collection c) | 向集合中添加另一个集合c的所有元素,添加成功后返回true。 |
void clear() | 清除集合中的所有元素,将集合长度变为0。 |
boolean contains(Object o) | 判断集合是否存在指定的元素。 |
boolean containsAll(Collection c) | 判断一个集合是否包含另一个集合c的所有元素。 |
boolean isEmpty() | 判断集合是否为空。 |
Iterator iterator() | 返回一个Iterator对象,用于遍历集合中的元素。 |
boolean remove(Object o) | 从集合中删除一个指定的元素,当集合中包含一个或多个元素o时,该方法只会删除第一个符合条件的元素,删除成功后返回true。 |
boolean removeAll(Collection c) | 从集合中删除所有在集合c中出现的元素,删除成功后返回true。 |
boolean retainAll(Collection c) | 从集合中删除所有不在集合c里的元素,删除成功后返回true。 |
int size() | 返回集合的元素个数。 |
Object[] toArray() | 将集合转换成一个数组,所有的集合元素会变成对应的数组元素。 |
因为Collection是一个接口,所以我们不能对其直接进行实例化操作,上述表格中的这些方法,我们需要先创建出某个具体的实现类对象进行调用,比如调用ArrayList对象的相关方法。
三. Iterator迭代器
1. 简介
我们可以使用Iterator迭代器对集合进行遍历,Iterator迭代器是集合类的输出接口,主要用于进行遍历输出(即迭代访问)Collection集合中的每个元素。迭代器是集合接口的父接口,子类实现Collection接口时也必须实现Iterator接口。不同的List对象调用iterator()方法时,会返回不同实现的Iterator对象,该Iterator对象对集合总是具有最高的访问效率。
2. 迭代原理
所谓的迭代,其实是一个重复反馈过程的活动,也就是一遍又一遍地执行相似的任务,其目的通常是为了逼近所需的目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果又会作为下一次迭代的初始值。迭代器的工作原理如下图所示:
3. 基本使用
Iterator迭代器的使用其实也很简单,主要是有两个常用方法,如下所示:
● boolean hasNext(): 该方法用于判断集合中是否还有下一个元素;
● E next(): 该方法用于返回集合的下一个元素。
import java.util.Iterator;import java.util.List;public class Demo02 {public static void main(String[] args) {//List遍历方式一,普通for循环:List list = List.of("java", "大数据", "壹壹哥");for(int i=0;iSystem.out.println("遍历方式一,值="+list.get(i));}//List遍历方式二,迭代器:Iterator it = list.iterator();while(it.hasNext()){//取出下一个值String value = it.next();System.out.println("遍历方式二,值="+value);}//List遍历方式三,增强for循环:内部会自动使用Iteratorfor(String item:list) {System.out.println("遍历方式三,item="+item);}}}
4. 潜在问题
我们在使用迭代器时,有可能会出现如下若干问题需要我们注意:
● 迭代器迭代完成后,迭代器的位置在最后一位,所以迭代器只能迭代一次;
● 迭代器在迭代时,不要多次调用next()方法,否则可能会出现NoSuchElementException异常;
● 迭代器在迭代时,不能向集合中添加或删除元素,否则会出现ConcurrentModificationException异常。
四. 结语
至此,就把集合的基本情况给大家介绍完了,请大家梳理记忆集合类之间的这些关系。
我们在面试时,经常会有面试官问我们Java中有哪些集合类,它们是什么关系和区别,其实考察的就是今天的内容。那么今天的重点内容,给大家总结如下:
● Java集合类定义在java.util包中;
● Java集合的接口和实现类进行了分离,支持泛型;
● Java集合分为Collection和Map两大类,具体又分为List、Set、Queue和Map等子类;
● Java集合使用Iterator遍历集合,我们在开发时尽量不要使用遗留接口。
以上就是本文的全部内容啦,有不明白的地方欢迎大家一起交流讨论!
更多Java技术类干货,可以戳我主页
Java系列免费学习视频,B站搜索千锋教育即可观看
视频传送门:千锋教育B站主页