本文将为大家详细讲解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等。

类名称作用
ArrayListArrayList是基于可变数组实现的List,优点是可以进行快速地随机访问,查询效率高,但数据的增删改操作速度较慢。
LinkedListLinkedList是基于链表实现的List,对顺序访问进行了优化,数据的增删改操作效率较高,但随机访问的速度相对较慢。
HashSetHashSet是基于HashMap实现的Set,底层使用HashMap来保存所有的数据元素,内部实现比较简单,优化了査询速度。
TreeSetTreeSet是一个有序的Set子类,我们可以从中提取一个有序序列。
HashMapHashMap是根据哈希算法来进行键值对存取的Map集合,属于双列集合。
TreeMapTreeMap是可以对键对象进行排序的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站主页