• 66从零开始学Java之集合中的Collection体系


    作者:孙玉昌,昵称【一一哥】,另外【壹壹哥】也是我哦

    千锋教育高级教研员、CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者

    前言

    截止到今天,我们《从零开始学Java系列》的文章已经要到一个新的阶段了。在此之前,我们学习了Java里的各种基础知识,包括环境配置、基本语法、分支循环、数组、常用类等。而从今天开始,壹哥要给大家介绍另一个我们开发时特别常用的知识点--集合!

    其实我们之前学习的数组也是集合的一种,但数组的结构比较简单,并且自身也存在一些天生的缺陷,比如数组的长度是不可修改的。然而在很多情况下,我们在开发时并不能直接确定数据的数量,这就导致我们在开发时不能频繁地使用数组。所以就需要有一种新的存储数据的结构出现,这就是集合类存在的意义。接下来我们就先从整体上来了解一下集合是怎么回事,希望大家能够通过最近的几篇文章,熟练掌握集合的使用和原理。

    -----------------------------------------------前戏已做完,精彩即开始---------------------------------------------

    全文大约【4400】字,不说废话,只讲可以让你学到技术、明白原理的纯干货!本文带有丰富的案例及配图视频,让你更好地理解和运用文中的技术概念,并可以给你带来具有足够启迪的思考......

    配套开源项目资料

    Github:

    GitHub - SunLtd/LearnJava

    Gitee:

    一一哥/从零开始学Java

    一. 集合简介

    1. 数组缺陷

    只要是内部能够持有若干个其他Java对象,并对外提供了访问接口的Java对象,都属于集合。根据之前我们对数组的认知,数组其实也是一种集合,也可以将不同类型的数据存储起来。既然如此,我们为什么还要搞出来一种新的存储结构来进行数据的存储呢?这主要是因为数组自身存在一些缺陷。

    我们虽然可以使用数组来保存多个对象,但数组的长度不可变!也就是说,一旦我们在初始化数组时指定了数组的长度,这个数组的长度就不可变了。如果我们要保存数量不断变化的数据,使用数组的效率就会很差。

    另外数组也不能保存具有映射关系的数据。有时候我们想根据某个关键字查找到对应的值,数组是做不到的。比如我们想存储学生的成绩信息,”语文—89“,”数学—60“,数组就很难表达出这种一对一的映射关系。

    正因如此,数组难以满足我们在开发时的一些需求,所以这就需要有新的存储结构出现,那么集合也就应运而生了。

    External Player - 哔哩哔哩嵌入式外链播放器

    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等子类。

    External Player - 哔哩哔哩嵌入式外链播放器

    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对象的相关方法。

    External Player - 哔哩哔哩嵌入式外链播放器

    三. Iterator迭代器

    1. 简介

    我们可以使用Iterator迭代器对集合进行遍历,Iterator迭代器是集合类的输出接口,主要用于进行遍历输出(即迭代访问)Collection集合中的每个元素。迭代器是集合接口的父接口,子类实现Collection接口时也必须实现Iterator接口。不同的List对象调用iterator()方法时,会返回不同实现的Iterator对象,该Iterator对象对集合总是具有最高的访问效率。

    2. 迭代原理

    所谓的迭代,其实是一个重复反馈过程的活动,也就是一遍又一遍地执行相似的任务,其目的通常是为了逼近所需的目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果又会作为下一次迭代的初始值。迭代器的工作原理如下图所示:

    3. 基本使用

    Iterator迭代器的使用其实也很简单,主要是有两个常用方法,如下所示:

    • boolean hasNext():该方法用于判断集合中是否还有下一个元素;
    • E next():该方法用于返回集合的下一个元素。
    1. import java.util.Iterator;
    2. import java.util.List;
    3. public class Demo02 {
    4. public static void main(String[] args) {
    5. //List遍历方式一,普通for循环:
    6. List list = List.of("java", "大数据", "壹壹哥");
    7. for(int i=0;i
    8. System.out.println("遍历方式一,值="+list.get(i));
    9. }
    10. //List遍历方式二,迭代器:
    11. Iterator it = list.iterator();
    12. while(it.hasNext()){
    13. //取出下一个值
    14. String value = it.next();
    15. System.out.println("遍历方式二,值="+value);
    16. }
    17. //List遍历方式三,增强for循环:内部会自动使用Iterator
    18. for(String item:list) {
    19. System.out.println("遍历方式三,item="+item);
    20. }
    21. }
    22. }

    4. 潜在问题

    我们在使用迭代器时,有可能会出现如下若干问题需要我们注意:

    • 迭代器迭代完成后,迭代器的位置在最后一位,所以迭代器只能迭代一次;
    • 迭代器在迭代时,不要多次调用next()方法,否则可能会出现NoSuchElementException异常;
    • 迭代器在迭代时,不能向集合中添加或删除元素,否则会出现ConcurrentModificationException异常。

    -----------------------------------------------​​​​​​​正片已结束,来根事后烟---------------------------------------------

    四. 结语

    至此,壹哥就把集合的基本情况给大家介绍完了,请大家梳理记忆集合类之间的这些关系。我们在面试时,经常会有面试官问我们Java中有哪些集合类,它们是什么关系和区别,其实考察的就是今天的内容。那么今天的重点内容,壹哥给大家总结如下:

    • Java集合类定义在java.util包中;
    • Java集合的接口和实现类进行了分离,支持泛型;
    • Java集合分为Collection和Map两大类,具体又分为List、Set、Queue和Map等子类;
    • Java集合使用Iterator遍历集合,我们在开发时尽量不要使用遗留接口。

    另外如果你独自学习觉得有很多困难,可以加入壹哥的学习互助群,大家一起交流学习。

  • 相关阅读:
    SpringCloud Alibaba(二) - Sentinel,整合OpenFeign,GateWay服务网关
    系统与应用监控的缜密思路
    在Android中以ActivityResultLauncher方式进行页面跳转、传递参数、拍照或选择文件,以及调用系统应用打开各种类型的指定文件
    从XML配置角度理解Spring AOP
    Cisco交换机关于DHCP SNOOPING的配置指令
    [附源码]计算机毕业设计作业查重系统Springboot程序
    大模型算法(一):从Transformer到ViT再到LLaMA
    python datetime 返回一个时间段内的所有日期列表
    DDD落地:从美团抽奖平台,看DDD在大厂如何落地?
    B. Bin Packing Problem(线段树+multiset)
  • 原文地址:https://blog.csdn.net/syc000666/article/details/134530534