Wend看源码-Java-集合学习(List)

摘要

        本篇文章深入探讨了基于JDK 21版本的Java.util包中提供的多样化集合类型。在Java中集合共分类为三种数据结构:List、Set和Queue。本文将详细阐述这些数据类型的各自实现,并按照线程安全性进行分类,分别介绍非线程安全与线程安全的实现方式。接下来,我们将逐一探究这些集合类型的细节和应用。

Collection

图 1 基本集合类图

        如图1 所示,jdk21 的基本集合类型共包含了三个类,分别是Collection 接口、SequencedCollection 接口和 AbstractCollection 抽象类。

Collection

  Collection 接口是 Java 集合框架中的根接口,它定义了集合的基本操作和属性。所有其他集合类型,如 ListSet 和 Queue,都继承自 Collection 接口。

  • 主要功能:

    • 添加元素:add(E e)addAll(Collection<? extends E> c)

    • 删除元素:remove(Object o)removeAll(Collection<?> c)clear()

    • 检查元素:contains(Object o)containsAll(Collection<?> c)

    • 获取大小:size()

    • 迭代元素:iterator()

    • 转换为数组:toArray()

SequencedCollection

        SequencedCollection在Jdk21中被引入,作为集合框架的拓展。这个接口扩展了 Collection 接口,并添加了与顺序相关的方法,允许元素按照特定的顺序进行迭代。

  • 主要功能:

    • 保持插入顺序:SequencedCollection 保证元素的迭代顺序与它们被添加到集合中的顺序相同。

    • 提供顺序相关操作:例如,first() 和 last() 方法可以分别返回集合中的第一个和最后一个元素。

  • 主要方法:

    • default void addFirst(E e):实现是调用 add(0, e),将元素添加到列表头部,存在 null 相关以及不支持操作时的异常情况。

    • default void addLast(E e):实现是调用 add(e),将元素添加到列表尾部,同样有相应异常情况。

    • default E getFirst():若列表不为空返回 get(0) 的结果,否则抛出 NoSuchElementException

    • default E getLast():若列表不为空返回 get(size() - 1) 的结果,否则抛出 NoSuchElementException

    • default E removeFirst():若列表不为空返回 remove(0) 的结果,否则抛出 NoSuchElementException,也存在不支持操作时的异常情况。

    • default E removeLast():若列表不为空返回 remove(size() - 1) 的结果,否则抛出 NoSuchElementException,同样有不支持操作时的异常情况。

    • default List<E> reversed():返回列表的逆序视图,以 List 形式呈现,委托原列表进行操作。

AbstractCollection

  AbstractCollection 类是一个抽象类,提供了 Collection 接口的基本实现。这个类旨在简化实现自定义集合类型的过程,因为它实现了 Collection 接口中的大多数方法,只留下 iterator() 和 size() 方法需要子类去实现。

  • 主要特点:

    • 作为自定义集合类型的基类:通过继承 AbstractCollection,可以减少实现一个集合类型所需的工作量。

    • 提供了实用方法:比如 addAll()removeAll()containsAll() 等,这些方法在许多集合类型中是通用的。

在使用 AbstractCollection 时,子类必须实现以下两个方法:

  • iterator(): 返回一个迭代器,用于遍历集合中的元素。

  • size(): 返回集合中元素的数量。

List 集合

图2  java-list类型数据结构类图

List 接口        

        如图2 所示,List 接口继承SequencedCollection接口,所有的List 拓展数据类型均需要实现List 接口。以下是关于List 接口的重要方法总结。

  • size:返回列表中元素的数量,如果元素数量超过 Integer.MAX_VALUE,则返回 Integer.MAX_VALUE

  • isEmpty:若列表不包含任何元素,则返回 true,反之返回 false

  • contains:判断列表中是否包含指定元素,若存在至少一个元素 e 使得 Objects.equals(o, e) 成立,则返回 true,否则返回 false。不过在元素类型不兼容或指定元素为 null (列表不允许 null 元素时)会抛出相应异常。

  • iterator:返回按正确顺序遍历列表元素的迭代器。

  • toArray:返回一个包含列表所有元素的数组(按从第一个到最后一个元素的顺序),返回的数组与列表没有引用关联,调用者可自由修改该数组,起到了数组和集合 API 之间的桥梁作用。

  • get:返回列表中指定位置的元素,若索引越界则抛出异常。

  • set:用指定元素替换列表中指定位置的元素,返回被替换的原来的元素,在不支持替换操作、元素类型不允许、元素为 null (列表不允许时)、元素属性不符合要求、索引越界等情况下会抛异常。

  • add:将指定元素追加到列表末尾,不同列表实现可能对添加元素有限制,在不支持添加操作、元素类型不允许、元素为 null (列表不允许时)或元素自身属性不符合要求等情况下会抛出相应异常。

  • remove:若列表中存在指定元素,则移除其第一次出现的位置对应的元素,若列表不包含该元素则列表不变,移除成功返回 true,否则返回 false,同样在元素类型不兼容或指定元素为 null (列表不允许 null 元素时)会抛出异常,并且若不支持移除操作也会抛异常。

  • containsAll:判断列表是否包含指定集合中的所有元素,在元素类型不兼容、指定集合包含 null 元素(列表不允许时)或集合本身为 null 等情况下会抛异常。

  • addAll:将指定集合中的所有元素按其迭代器返回的顺序追加到列表末尾,若在操作过程中指定集合被修改则行为未定义,在不支持添加操作、元素类型不允许、集合包含 null 元素(列表不允许时)或元素自身属性不符合要求等情况会抛异常。

  • removeAll:从列表中移除所有包含在指定集合中的元素,在不支持移除操作、元素类型不兼容、列表含 null 元素(指定集合不允许时)或集合为 null 等情况会抛异常。

  • replaceAll:使用给定操作符对列表的每个元素进行替换,若列表不可修改、操作符为 null 或者操作结果为 null (列表不允许 null 元素时)会抛异常。

  • sort:根据指定的比较器对列表进行排序,排序是稳定的,要求列表元素能用指定比较器相互比较,在比较出现类型不兼容、列表迭代器不支持 set 操作、比较器违反相关约定等情况下会抛异常。

  • clear:移除列表中的所有元素,若不支持该清除操作则会抛出异常。

  • indexOf:返回指定元素在列表中第一次出现的索引,如果列表不包含该元素则返回 -1,在元素类型不兼容或指定元素为 null (列表不允许 null 元素时)会抛异常。

  • lastIndexOf:返回指定元素在列表中最后一次出现的索引,如果列表不包含该元素则返回 -1,同样在元素类型不兼容或指定元素为 null (列表不允许 null 元素时)会抛异常。

  • subList:返回列表中指定范围(含 fromIndex,不含 toIndex)的视图,返回的列表由原列表支持,两者的非结构性变化会相互影响,若原列表进行了非通过返回列表的结构性修改则语义会未定义,端点索引值非法时抛出异常。

  • spliterator():创建一个用于遍历列表元素的分割迭代器,有不同的创建逻辑取决于列表是否是 RandomAccess 等情况,并且默认实现额外报告了一些特性。

  • of:返回一个包含X个元素的不可修改列表,从 Java 9 起可用。

  • copyOf:返回一个包含给定集合元素的不可修改列表(按集合迭代顺序),集合不能为 null 且不能包含 null 元素,从 Java 10 起可用,若给定集合本身是不可修改列表,通常不会创建副本。

AbstractList

        AbstractList是一个抽象类,它实现了List接口的大部分方法,为具体的列表实现类提供了一个基础框架。通过继承AbstractList,开发人员可以更方便地创建自定义的列表类,只需关注需要特殊实现的方法即可。

AbstractSequentialList

        该类主要侧重于顺序访问元素的列表实现。与AbstractList(更侧重于随机访问列表)不同,AbstractSequentialList假定列表的访问通常是顺序进行的,例如通过迭代器逐个元素地访问,而不是通过索引进行随机访问。

ArrayList

  ArrayList是基于数组实现的动态列表,它是Java集合框架中最常用的列表类之一。可以存储任意类型的对象,并且能够自动调整大小以适应元素的添加和删除。

  • 主要特点

    • 随机访问高效:由于其内部是基于数组实现的,所以支持快速的随机访问。通过索引访问元素的时间复杂度为。例如,使用get(int index)方法获取指定索引位置的元素非常高效。

    • 动态扩容:当添加元素时,如果数组已满,ArrayList会自动创建一个更大的新数组,并将原有元素复制到新数组中。例如,初始容量为 10,当添加第 11 个元素时,会创建一个新的更大的数组来存储元素。

    • 顺序存储:元素在内存中是顺序存储的,这使得遍历元素比较高效。可以使用普通的for循环或者增强型for循环来遍历ArrayList中的元素。

  • 适用场景

    • 适用于需要频繁进行随机访问和遍历操作的场景。比如,在数据查询较多而插入和删除操作相对较少的情况下,如存储用户信息列表,在需要根据用户 ID(假设用户 ID 是列表索引)快速获取用户信息时,ArrayList是一个很好的选择。

LinkedList

  LinkedList是一个双向链表实现的列表,它实现了List接口,同时也实现了Deque接口,因此可以作为队列、双端队列和栈来使用。

  • 主要特点

    • 高效的插入和删除操作(非随机访问):在链表中插入和删除元素的操作比较高效。例如,在链表中间插入一个元素,只需要修改相邻节点的引用即可,时间复杂度为(不考虑查找插入位置的时间)。

    • 双端操作支持:作为双端队列,它提供了在头部和尾部添加、删除元素的方法,如addFirst()addLast()removeFirst()removeLast()等,方便在两端进行操作。

    • 顺序访问特性:遍历链表需要从头部或尾部开始,逐个节点访问,不支持像ArrayList那样的随机访问。通过迭代器或者for循环遍历链表的时间复杂度为,其中n是链表的长度。

  • 适用场景

    • 适用于需要频繁进行插入和删除操作,特别是在列表两端进行操作的场景。例如,在实现一个任务队列时,新任务可以添加到队列尾部,而当任务完成时可以从队列头部移除任务,LinkedList可以很好地满足这种需求。

线程安全的List

Vector (Deprecated)

  Vector 是Java 早期线程安全的List 实现。Vector的方法都被synchronized关键字修饰,在多线程环境下可以保证数据的一致性和完整性,同一时刻只有一个线程能够访问和修改Vector中的元素。在现代的java开发中,更推荐使用java.util.concurrent包中的数据结构和方法来实现线程安全的List集合。

Stack (Deprecated)

  Stack类是基于Vector实现的后进先出(LIFO)的数据结构,它继承自Vector,并在Vector的基础上提供了专门用于栈操作的方法。不过,在 Java 6 之后,推荐使用Deque接口及其实现类(如ArrayDeque)来实现栈的功能,因为它们提供了更简洁和一致的栈操作方法。       

CopyOnWriteArrayList

   CopyOnWriteArrayListjava.util.concurrent包中的一个类,它实现了List接口,用于在多线程环境下提供线程安全的列表操作。其核心原理是 “写时复制”(Copy - On - Write)。

        当对列表进行修改操作(如添加、删除或设置元素)时,它会先复制一份当前的数组(底层存储结构是数组),然后在这个新的副本数组上进行修改操作。修改完成后,再将内部的数组引用指向新的数组。而读取操作是在原始数组上进行的,不需要进行加锁操作,这样就可以实现高并发的读操作。

线程安全特性
  • 并发读操作的高效性:在多个线程同时读取CopyOnWriteArrayList中的元素时,由于读操作不需要加锁,所以可以同时进行,大大提高了读取的效率。例如,在一个多线程的 Web 服务器中,多个线程可能需要同时读取配置信息列表,CopyOnWriteArrayList可以很好地支持这种高并发的读取场景。
  • 写操作的线程安全保证:在进行写操作时,虽然会涉及到数组的复制和修改,但通过这种 “写时复制” 机制,保证了同一时刻只有一个写操作在进行,避免了多个线程同时修改数据而导致的数据不一致问题。例如,当一个线程在添加元素时,其他线程要么在读取原始数组(此时还未更新引用),要么在等待当前写操作完成后再进行自己的操作。
适用场景
  • 读多写少的场景:因为读操作不需要加锁,所以在读取操作远远多于写入操作的情况下,CopyOnWriteArrayList能够提供很好的性能。比如,在一个系统中,配置信息列表被多个线程频繁读取,但很少被修改,使用CopyOnWriteArrayList就非常合适。
  • 遍历操作的稳定性需求场景:在遍历列表的过程中,如果不希望受到其他线程修改操作的影响,CopyOnWriteArrayList是一个不错的选择。因为遍历是在原始数组上进行的,即使其他线程正在进行写操作,也不会干扰当前的遍历操作,保证了遍历数据的稳定性。
局限性
  • 内存占用:由于写操作需要复制数组,这可能会导致内存占用较大。如果列表中的元素数量很大,每次写操作的复制成本就会很高。例如,一个包含大量数据的列表频繁进行写操作,会消耗大量的内存来存储复制后的数组。
  • 数据实时性稍差:因为读取操作是基于原始数组的,在写操作完成之前,读取的可能是旧的数据。对于对数据实时性要求极高的场景,可能不太适用。比如,在一个实时数据处理系统中,要求数据修改后能立即被读取到最新值,CopyOnWriteArrayList可能无法满足这种需求。

参考文献

java集合超详解-腾讯云开发者社区-腾讯云

豆包

相关文章推荐

Wend看源码-Java-集合学习(Set)-CSDN博客
Wend看源码-Java-集合学习(Queue)-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/944087.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

人工智能知识分享第二天-机器学习之KNN算法

KNN算法 KNN算法简介 KNN算法思想 K-近邻算法&#xff08;K Nearest Neighbor&#xff0c;简称KNN&#xff09;。比如&#xff1a;根据你的“邻居”来推断出你的类别 KNN算法思想&#xff1a;如果一个样本在特征空间中的 k 个最相似的样本中的大多数属于某一个类别&#xff…

b站ip属地评论和主页不一样怎么回事

在浏览B站时&#xff0c;细心的用户可能会发现一个有趣的现象&#xff1a;某些用户的评论IP属地与主页显示的IP属地并不一致。这种差异引发了用户的好奇和猜测&#xff0c;究竟是什么原因导致了这种情况的发生呢&#xff1f;本文将对此进行深入解析&#xff0c;帮助大家揭开这一…

Robyn+Vue3+wangEditor打造个人笔记

Github&#xff1a;https://github.com/gwt805/MYNotes Gitee: https://gitee.com/gwt805/MYNotes GitCode: https://gitcode.com/gwt805/MYNotes/overview

BGP路由反射器,解决路由黑洞问题

路由反射器解决路由黑洞问题 路由反射器解决路由黑洞问题 路由黑洞的产生路由黑洞的解决办法路由反射器解决黑洞问题基本配置配置反射器前查看路由配置路由反射器配置反射器后查看路由路由黑洞的产生 根据BGP建立邻居的规则,只要TCP可达便可建立邻居系。如下图所示: AR2、AR…

JavaFX FXML模式下的布局

常见布局方式概述 在 JavaFX FXML 模式下&#xff0c;有多种布局方式可供选择。这些布局方式可以帮助您有效地组织和排列 UI 组件&#xff0c;以创建出美观且功能良好的用户界面。常用布局容器及布局方式 BorderPane 布局 特点&#xff1a;BorderPane 将空间划分为五个区域&…

二叉树的深搜_求根节点到叶节点数字之和_验证二叉搜索树_二叉树的所有路径

2331. 计算布尔二叉树的值 二叉树遍历可以用递归来解决,该问题的简单子问题是 知道左右孩子的值&#xff0c;再根据| &返回true/false。左子树右子树的值交给dfs解决。 终止条件就是到达叶子节点&#xff0c;即没有左右孩子&#xff0c;因为是完全二叉树 所以只需要判断一个…

Jupyter占用内存高问题排查解决

前言 前段时间我们上线了实例内存预警功能&#xff0c;方便大家更好地管理服务器内存资源。那么&#xff0c;也有同学会问&#xff0c;如果收到系统通知&#xff0c;我该怎么做呢&#xff1f;系统提示交换内存占用过高&#xff0c;但是又不知道是哪些程序占用的&#xff0c;怎么…

python下载,安装,环境配置

下载地址&#xff1a;Python Windows版本下载| Python中文网 官网 选择路径 安装完成 检测安装是否成功 使用 winr 启动运行对话框&#xff0c;输入 cmd 进入命令行。 输入pip list 输入 where python 查看 python.exe 的路径 环境配置 winr 打开运行对话框&#xff0c;输入 …

抓取手机HCI日志

荣耀手机 1、打开开发者模式 2、开启HCI、ADB调试 3、开启AP LOG 拨号界面输入*##2846579##* 4、蓝牙配对 5、抓取log adb pull /data/log/bt ./

IDEA 搭建 SpringBoot 项目之配置 Maven

目录 1?配置 Maven 1.1?打开 settings.xml 文件1.2?配置本地仓库路径1.3?配置中央仓库路径1.4?配置 JDK 版本1.5?重新下载项目依赖 2?配置 idea 2.1?在启动页打开设置2.2?配置 Java Compiler2.3?配置 File Encodings2.4?配置 Maven2.5?配置 Auto Import2.6?配置 C…

算法比赛汇总

数据科学竞赛平台网站整理 | ✨DEEPAI数据分析

深入研究物理光学传播和 ZBF 文件

物理光学传播特征 Zemax 中的物理光学传播 (POP) 是一种用于模拟和分析光在光学系统中传播时的行为的工具。与假设理想化几何射线的射线追踪不同&#xff0c;POP 考虑了光的波动性质&#xff0c;包括衍射和干涉。这使得它对于设计和分析显微镜、激光器等高精度光学系统以及其他…

【Java数据结构】栈和队列

栈&#xff08;Stack&#xff09; 栈的概念 栈是一种特殊的线性表&#xff0c;只允许在一端进行插入和删除。栈遵循后进先出&#xff0c;分别在栈顶删除、栈底插入。 栈的常用方法 栈的一些方法&#xff0c;例如&#xff1a;出栈、入栈、取栈顶元素、是否为空、栈中元素个数等…

StarRocks元数据无法合并

一、先说结论 如果您的StarRocks版本在3.1.4及以下&#xff0c;并且使用了metadata_journal_skip_bad_journal_ids来跳过某个异常的journal&#xff0c;结果之后就出现了FE的元数据无法进行Checkpoint的现象&#xff0c;那么选择升级版本到3.1.4以上&#xff0c;就可以解决。 …

使用qrcode.vue生成当前网页的二维码(H5)

使用npm&#xff1a; npm install qrcode.vue 使用yarn&#xff1a; yarn add qrcode.vue package.json&#xff1a; 实现&#xff1a; <template><div class"code"><qrcode-vue :value"currentUrl" :size"size" render-as&…

【STM32】RTT-Studio中HAL库开发教程十:EC800M-4G模块使用

文章目录 一、简介二、模块测试三、OneNet物联网配置四、完整代码五、测试验证 一、简介 EC800M4G是一款4G模块&#xff0c;本次实验主要是进行互联网的测试&#xff0c;模块测试&#xff0c;以及如何配置ONENET设备的相关参数&#xff0c;以及使用STM32F4来测试模块的数据上报…

STM32完全学习——FATFS0.15移植SD卡

一、下载FATFS源码 大家都知道使用CubMAX可以很快的将&#xff0c;FATFS文件管理系统移植到单片机上&#xff0c;但是别的芯片没有这么好用的工具&#xff0c;就需要自己从官网下载源码进行移植。我们首先解决SD卡的驱动问题&#xff0c;然后再移植FATFS文件管理系统。 二、SD…

【知识】cuda检测GPU是否支持P2P通信及一些注意事项

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 代码流程 先检查所有GPU之间是否支持P2P通信&#xff1b;然后尝试启用GPU之间的P2P通信&#xff1b;再次检查所有GPU之间是否支持P2P通信。 test.cu&…

Mysql大数据量表分页查询性能优化

一、模拟场景 1、产品表t_product,数据量500万+ 2、未做任何优化前,cout查询时间大约4秒;LIMIT offset, count 时,offset 值较大时查询时间越久。 count查询 SELECT COUNT(*) AS total FROM t_product WHERE deleted = 0 AND tenant_id = 1 分页查询 SELECT * FROM t_…

go语言的成神之路-筑基篇-对文件的操作

目录 一、对文件的读写 Reader?接口 ?Writer接口 copy接口 bufio的使用 ioutil库? 二、cat命令 三、包 1. 包的声明 2. 导入包 3. 包的可见性 4. 包的初始化 5. 标准库包 6. 第三方包 ?7. 包的组织 8. 包的别名 9. 包的路径 10. 包的版本管理 四、go mo…