Java集合常见知识总结(上)
Java 集合概览
Java 集合,也叫作容器,主要是由两大接口派生而来:一个是 Collection接口,主要用于存放单一元素;另一个是 Map 接口,主要用于存放键值对。对于Collection 接口,下面又有三个主要的子接口:List、Set 、 Queue

图中只列举了主要的继承派生关系,并没有列举所有关系。比方省略了
AbstractList,NavigableSet等抽象类以及其他的一些辅助类,如想深入了解,可自行查看源码
List, Set, Queue, Map 四者的区别
-
List(对付顺序的好帮手): 存储的元素是有序的、可重复的 -
Set(注重独一无二的性质): 存储的元素不可重复的 -
Queue(实现排队功能的叫号机): 按特定的排队规则来确定先后顺序,存储的元素是有序的、可重复的 -
Map(用 key 来搜索的专家): 使用键值对(key-value)存储,类似于数学上的函数 y=f(x),"x" 代表 key,"y" 代表 value,key 是无序的、不可重复的,value 是无序的、可重复的,每个键最多映射到一个值
集合框架底层数据结构总结
List
-
ArrayList:Object[]数组。详细可以查看:ArrayList 源码分析 -
Vector:Object[]数组 -
LinkedList:双向链表(JDK1.6 之前为循环链表,JDK1.7 取消了循环)。详细可以查看:LinkedList 源码分析
Set
-
HashSet(无序,唯一): 基于HashMap实现的,底层采用HashMap来保存元素 -
LinkedHashSet:LinkedHashSet是HashSet的子类,并且其内部是通过LinkedHashMap来实现的 -
TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树)
Queue
-
PriorityQueue:Object[]数组来实现小顶堆。详细可以查看:PriorityQueue 源码分析 -
DelayQueue:PriorityQueue。详细可以查看:DelayQueue 源码分析 -
ArrayDeque: 可扩容动态双向数组
Map
-
HashMap:JDK1.8 之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8 以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。详细可以查看:HashMap 源码分析 -
LinkedHashMap:LinkedHashMap继承自HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。详细可以查看:LinkedHashMap 源码分析 -
Hashtable:数组+链表组成的,数组是Hashtable的主体,链表则是主要为了解决哈希冲突而存在的 -
TreeMap:红黑树(自平衡的排序二叉树)
如何选用集合
当我们需要存储一组类型相同的数据时,数组是最常用且最基本的容器之一。但是,使用数组存储对象存在一些不足之处,因为在实际开发中,存储的数据类型多种多样且数量不确定。这时,Java 集合就派上用场了。与数组相比,Java 集合提供了更灵活、更有效的方法来存储多个数据对象。Java 集合框架中的各种集合类和接口可以存储不同类型和数量的对象,同时还具有多样化的操作方式。相较于数组,Java 集合的优势在于它们的大小可变、支持泛型、具有内建算法等。总的来说,Java 集合提高了数据的存储和处理灵活性,可以更好地适应现代软件开发中多样化的数据需求,并支持高质量的代码编写
List
ArrayList 和 Array(数组)的区别?
ArrayList 内部基于动态数组实现,比 Array(静态数组) 使用起来更加灵活:
-
ArrayList会根据实际存储的元素动态地扩容或缩容,而Array被创建之后就不能改变它的长度了 -
ArrayList允许你使用泛型来确保类型安全,Array则不可以 -
ArrayList中只能存储对象。对于基本类型数据,需要使用其对应的包装类(如 Integer、Double 等)。Array可以直接存储基本类型数据,也可以存储对象 -
ArrayList支持插入、删除、遍历等常见操作,并且提供了丰富的 API 操作方法,比如add()、remove()等。Array只是一个固定长度的数组,只能按照下标访问其中的元素,不具备动态添加、删除元素的能力 -
ArrayList创建时不需要指定大小,而Array创建时必须指定大小
ArrayList 和 Vector 的区别?(了解即可)
-
ArrayList是List的主要实现类,底层使用Object[]存储,适用于频繁的查找工作,线程不安全 -
Vector是List的古老实现类,底层使用Object[]存储,线程安全
Vector 和 Stack 的区别?(了解即可)
-
Vector和Stack两者都是线程安全的,都是使用synchronized关键字进行同步处理 -
Stack继承自Vector,是一个后进先出的栈,而Vector是一个列表
随着 Java 并发编程的发展,
Vector和Stack已经被淘汰,推荐使用并发集合类(例如ConcurrentHashMap、CopyOnWriteArrayList等)或者手动实现线程安全的方法来提供安全的多线程操作支持
ArrayList 可以添加 null 值吗
ArrayList 中可以存储任何类型的对象,包括 null 值。不过,不建议向ArrayList 中添加 null 值, null 值无意义,会让代码难以维护比如忘记做判空处理就会导致空指针异常
ArrayList<String> listOfStrings = new ArrayList<>();
listOfStrings.add(null);
listOfStrings.add("java");
System.out.println(listOfStrings);
输出:
[null, java]
ArrayList 插入和删除元素的时间复杂度?
对于插入:
-
头部插入:由于需要将所有元素都依次向后移动一个位置,因此时间复杂度是 O(n)
-
尾部插入:当
ArrayList的容量未达到极限时,往列表末尾插入元素的时间复杂度是 O(1),因为它只需要在数组末尾添加一个元素即可;当容量已达到极限并且需要扩容时,则需要执行一次 O(n) 的操作将原数组复制到新的更大的数组中,然后再执行 O(1) 的操作添加元素 -
指定位置插入:需要将目标位置之后的所有元素都向后移动一个位置,然后再把新元素放入指定位置。这个过程需要移动平均 n/2 个元素,因此时间复杂度为 O(n)
对于删除:
-
头部删除:由于需要将所有元素依次向前移动一个位置,因此时间复杂度是 O(n)
-
尾部删除:当删除的元素位于列表末尾时,时间复杂度为 O(1)
-
指定位置删除:需要将目标元素之后的所有元素向前移动一个位置以填补被删除的空白位置,因此需要移动平均 n/2 个元素,时间复杂度为 O(n)
举例:
// ArrayList的底层数组大小为10,此时存储了7个元素 +---+---+---+---+---+---+---+---+---+---+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | | | +---+---+---+---+---+---+---+---+---+---+0 1 2 3 4 5 6 7 8 9 // 在索引为1的位置插入一个元素8,该元素后面的所有元素都要向右移动一位 +---+---+---+---+---+---+---+---+---+---+ | 1 | 8 | 2 | 3 | 4 | 5 | 6 | 7 | | | +---+---+---+---+---+---+---+---+---+---+0 1 2 3 4 5 6 7 8 9 // 删除索引为1的位置的元素,该元素后面的所有元素都要向左移动一位 +---+---+---+---+---+---+---+---+---+---+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | | | +---+---+---+---+---+---+---+---+---+---+0 1 2 3 4 5 6 7 8 9
LinkedList 插入和删除元素的时间复杂度?
-
头部插入/删除:只需要修改头结点的指针即可完成插入/删除操作,因此时间复杂度为 O(1)
-
尾部插入/删除:只需要修改尾结点的指针即可完成插入/删除操作,因此时间复杂度为 O(1)
-
指定位置插入/删除:需要先移动到指定位置,再修改指定节点的指针完成插入/删除,不过由于有头尾指针,可以从较近的指针出发,因此需要遍历平均 n/4 个元素,时间复杂度为 O(n)
假如我们要删除节点 9 的话,需要先遍历链表找到该节点。然后,再执行相应节点指针指向的更改

LinkedList 为什么不能实现 RandomAccess 接口
RandomAccess 是一个标记接口,用来表明实现该接口的类支持随机访问(即可以通过索引快速访问元素)。由于 LinkedList 底层数据结构是链表,内存地址不连续,只能通过指针来定位,不支持随机快速访问,所以不能实现 RandomAccess 接口
ArrayList 与 LinkedList 区别
-
是否保证线程安全:
ArrayList和LinkedList都是不同步的,也就是不保证线程安全 -
底层数据结构:
ArrayList底层使用的是Object数组;LinkedList底层使用的是 双向链表 数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!) -
插入和删除是否受元素位置的影响:
-
ArrayList采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)方法的时候,ArrayList会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element)),时间复杂度就为 O(n)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作 -
LinkedList采用链表存储,所以在头尾插入或者删除元素不受元素位置的影响(add(E e)、addFirst(E e)、addLast(E e)、removeFirst()、removeLast()),时间复杂度为 O(1),如果是要在指定位置i插入和删除元素的话(add(int index, E element),remove(Object o),remove(int index)), 时间复杂度为 O(n) ,因为需要先移动到指定位置再插入和删除
-
-
是否支持快速随机访问:
LinkedList不支持高效的随机元素访问(底层是链表结构,只支持使用指针访问),而ArrayList(实现了RandomAccess接口) 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法) -
内存空间占用:
ArrayList的空间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)
我们在项目中一般是不会使用到 LinkedList 的,需要用到 LinkedList 的场景几乎都可以使用 ArrayList 来代替,并且,性能通常会更好!就连 LinkedList 的作者约书亚 · 布洛克(Josh Bloch)自己都说从来不会使用 LinkedList

不要下意识地认为 LinkedList 作为链表就最适合元素增删的场景。我在上面也说了,LinkedList 仅仅在头尾插入或者删除元素的时候时间复杂度近似 O(1),其他情况增删元素的平均时间复杂度都是 O(n)
双向链表和双向循环链表
双向链表: 包含两个指针,一个 prev 指向前一个节点,一个 next 指向后一个节点

双向循环链表: 最后一个节点的 next 指向 head,而 head 的 prev 指向最后一个节点,构成一个环

RandomAccess 接口
public interface RandomAccess {
}
查看源码我们发现实际上 RandomAccess 接口中什么都没有定义。所以,在我看来 RandomAccess 接口不过是一个标识罢了。标识什么? 标识实现这个接口的类具有随机访问功能。
在 binarySearch() 方法中,它要判断传入的 list 是否 RandomAccess 的实例,如果是,调用indexedBinarySearch()方法,如果不是,那么调用iteratorBinarySearch()方法
public static <T>int binarySearch(List<? extends Comparable<? super T>> list, T key) {if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)return Collections.indexedBinarySearch(list, key);elsereturn Collections.iteratorBinarySearch(list, key);}
ArrayList 实现了 RandomAccess 接口, 而 LinkedList 没有实现。为什么呢?我觉得还是和底层数据结构有关!ArrayList 底层是数组,而 LinkedList 底层是链表。数组天然支持随机访问,时间复杂度为 O(1),所以称为快速随机访问。链表需要遍历到特定位置才能访问特定位置的元素,时间复杂度为 O(n),所以不支持快速随机访问。ArrayList 实现了 RandomAccess 接口,就表明了他具有快速随机访问功能
RandomAccess接口只是标识,并不是说ArrayList实现RandomAccess接口才具有快速随机访问功能!!!
相关文章:
Java集合常见知识总结(上)
Java 集合概览 Java 集合,也叫作容器,主要是由两大接口派生而来:一个是 Collection接口,主要用于存放单一元素;另一个是 Map 接口,主要用于存放键值对。对于Collection 接口,下面又有三个主要的…...
【算法】力扣:K个一组反转链表
前置知识 数据结构-链表反转部分链表算法题的手写栈使用 难度: 初阶:使用容器, 难度中等。进阶:纯coding修改指针 ,难度中等,虽然leetcode是困难题。不过更加注重细节。 题目:反转 k 组中的…...
Matlab报错——错误使用 vertcat
错误提示: 原因: 这个错误表明 segment_lengths 的维度和 0 不一致。在 MATLAB 中,有时,diff 函数的输出可能是行向量,而segment_lengths 应该是一个列向量才能与 0 正确连接。 解决方法: 使用转置操作 …...
【如何获取股票数据10】Python、Java等多种主流语言实例演示获取股票行情api接口之沪深A股历史分时KDJ数据获取实例演示及接口API说明文档
最近一两年内,股票量化分析逐渐成为热门话题。而从事这一领域工作的第一步,就是获取全面且准确的股票数据。因为无论是实时交易数据、历史交易记录、财务数据还是基本面信息,这些数据都是我们进行量化分析时不可或缺的宝贵资源。我们的主要任…...
进入 Searing-66 火焰星球:第一周游戏指南
Alpha 第四季已开启,穿越火焰星球 Searing-66,带你开启火热征程。准备好勇闯炙热的沙漠,那里有无情的高温和无情的挑战在等待着你。从高风险的烹饪对决到炙热的冒险,Searing-66 将把你的耐力推向极限。带上充足的水,天…...
考研论坛设计小程序ssm+论文源码调试讲解
2相关技术 2.1微信小程序 小程序是一种新的开放能力,开发者可以快速地开发一个小程序。小程序可以在微信内被便捷地获取和传播,同时具有出色的使用体验。尤其拥抱微信生态圈,让微信小程序更加的如虎添翼,发展迅猛。 2.2 MYSQL数据…...
JAVA笔记 | EasyExcel创建带有简单下拉框的导入模板
目录 前文 业务需求 具体代码 新增Handler 控制层 前文 SpringBoot笔记 | EasyExcel导入导出及基于模板导出_easyexcel模板导出-CSDN博客 业务需求 需要一个导出模板。一个列需要填写固定的值,或者方便用户填写。 自己需求,几个固定的字段对应固…...
【含开题报告+文档+PPT+源码】贫困儿童一对一扶贫帮扶系统设计与实现
开题报告 根据《中华人民共和国慈善法》第五十八条规定,慈善组织确定慈善受益人,应当坚持公开、公平、公正的原则,不得指定慈善组织管理人员的利害关系人作为受益人[2]。以上所列举的平台基本没有做到公开、公平、公正的原则,例如…...
多系统萎缩不慌张,这些维生素是你的“隐形盾牌”!️
在这个快节奏的时代,健康成为了我们最宝贵的财富。而对于多系统萎缩(MSA)的患者来说,合理的营养补充更是维护身体机能、提升生活质量的关键一步。今天,就让我们一起揭秘那些能够成为多系统萎缩患者“守护神”的维生素吧…...
IGFBP7:免疫治疗新靶点
前 言 胰岛素样生长因子结合蛋白7(IGFBP7)是胰岛素超家族的生长促进肽成员,可与胰岛素和IGF结合,调控细胞生长和分化。IGFBP7在不同的肿瘤类型中表现出抑制或促进肿瘤生长的“自相矛盾”活性。研究发现IGFBP7可增强治疗性单克隆…...
深度学习模型的架构与应用:技术解析与未来展望
1. 引言 深度学习(Deep Learning)模型是当代人工智能的核心技术之一,广泛应用于语音识别、计算机视觉、自然语言处理、推荐系统等众多领域。深度学习通过构建多层神经网络,能够自动从大规模数据中学习复杂的特征和模式,其应用成果不仅推动了技术的飞跃,也带来了智能化产…...
机器学习——主要分类
前言: 机器学习是人工智能的重要分支之一,它通过分析数据来构建模型,并通过这些模型进行预测、分类或决策。随着数据量的迅速增长,机器学习在多个领域展现出巨大的应用潜力,推动了科技的进步。根据学习方式和数据的使用…...
Java密封类(Sealed Classes)增强详解
Java密封类(Sealed Classes)增强详解 Java 17引入了一个重要的新特性——密封类(Sealed Classes),这一特性旨在增强Java编程语言的能力,提供了一种机制来限制哪些类可以继承一个给定的类或者实现一个给定的…...
鸿蒙如何自动生成二维码?QRCode组件
QRCode 用于显示单个二维码的组件。 说明: 该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 二维码组件的像素点数量与内容有关,当组件尺寸过小时,可能出现无法展示内容的情况&…...
【分布式知识】MapReduce详细介绍
文章目录 MapReduce概述1. MapReduce编程模型Map阶段Reduce阶段 2. Shuffle和Sort阶段3. MapReduce作业的执行流程4. MapReduce的优化和特性5. MapReduce的配置和调优 MapReduce局限性相关文献 MapReduce概述 MapReduce是一个分布式计算框架,它允许用户编写可以在大…...
JAVA八股
快速失败(fail-fast) 设计的目的是为了避免在遍历时对集合进行并发修改,从而引发潜在的不可预料的错误。 通过迭代器遍历集合时修改集合: 如果你使用Iterator遍历集合,然后直接使用集合的修改方法(如add(…...
关于武汉芯景科技有限公司的限流开关芯片XJ6288开发指南(兼容SY6288)
一、芯片引脚介绍 1.芯片引脚 二、系统结构图 三、功能描述 1.EN引脚控制IN和OUT引脚的通断 2.OCB引脚指示状态 3.过流自动断开...
指令:计算机的语言(五)
2.9 人机交互 ASCII与二进制 对应表略 字节转移指令 lbu:加载无符号字节,从内存中加载1个字节,放在寄存器最右边8位。 sb:存储字节指令,从寄存器的最右边取1个字节并将其写入内存。 复制1个字节顺序如下…...
C#笔记(1)
解决方案: 【1】组织项目:把项目放在放在一个解决方案中,统一开发,统一编译。 【2】管理项目:开发中的任何问题,在统一编译过程中,都能随时发现。也可以添加第三方的库文件。 命名空间: 命名空…...
SSDF攻击、防御与展望
摘要: 随着无线通信业务的不断发展,频域也越来越成为了一种珍贵的稀缺资源,与此同时,相应的无线电安全问题层出不穷,为无线通信造成了十分恶劣的影响,本文从深入理解认知无线电安全开始,对一些典…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...
