当前位置: 首页 > article >正文

【集合】底层原理实现及各集合之间的区别

文章目录

      • 集合
        • 2.1 介绍一下集合
        • 2.2 集合遍历的方法
        • 2.3 线程安全的集合
        • 2.4 数组和集合的区别
        • 2.5 ArrayList和LinkedList的区别
        • 2.6 ArrayList底层原理
        • 2.7 LinkedList底层原理
        • 2.8 CopyOnWriteArrayList底层原理
        • 2.9 HashSet底层原理
        • 2.10 HashMap底层原理
        • 2.11 HashTable底层原理
        • 2.12 ConcurrentHashMap底层原理
        • 2.13 HashMap和HashTable的区别
        • 2.14 HashTable和ConcurrentHashMap的区别
        • 2.15 HashMap、HashTable、ConcurrentHashMap的区别

集合

2.1 介绍一下集合

​ 集合分为单列集合Collection和双列集合Map,Collection有有序、可重复、有索引的List和无序、不可重复、无索引的Set,List实现有ArrayList、LinkedList,Set实现有HashSet、LinkedHashSet、TreeSet;
​ Map的特性是无序、键不可重复、无索引,其实现有HashMap、LinkedHashMap、TreeMap;(了解它们的特性)

在这里插入图片描述

2.2 集合遍历的方法
  • 普通for循环
  • 增强for循环(遍历单列集合,双列集合需要进行转换)
  • 迭代器
  • stream流foreach方法

1.List可以一边遍历一边修改吗?
在普通循环遍历中可以,但如果使用迭代器则不可以;因为迭代器在创建的时候会记录集合的modCount,如果使用集合的方法,modCount改变而迭代器内部的expectedModCount不会改变,从而导致抛出ConcurrentModificationException异常。

2.Map遍历需要使用到entrySet()或keySet()方法

2.3 线程安全的集合

java.util:

  • Vector:内部的方法都经过了synchronized修饰
  • HashTable:内部方法都经过了synchronized修饰,不支持null键和值

java.util.concurrent

  • ConcurrentHashMap
    jdk1.8之前:加的是分段锁,锁的是一部分,不同分段之间并发操作不影响
    jdk1.8之后:锁的是每一行,减少并发冲突的概率;put操作时,位置为空,则使用CAS操作设置值,位置不为空,使用synchronized申请锁,进行操作
  • ConcurrentSkipListMap:可排序的并发集合,能够在log(n)的时间内完成增删查改操作
  • ConcurrentSkipListSet:可排序的并发集合,基于ConcurrentSkipListMap实现
  • CopyOnWriteArraySet
  • CopyOnWriteArrayList:写操作时加锁ReentrantLock,复制原数组进行操作,而读操作则直接读原数组,实现了读写分离,提高了并发性能。

1.ArrayList是线程不安全的?
添加元素的步骤:
1)判断是否需要扩容
2)将size位置设置为待加入的值
3)size+1
可能导致数组下标越界元素覆盖数组大小和实际插入大小不一致,可以使用CopyOnWriteArrayList、Vector代替,使之变成线程安全的。

2.HashMap是线程不安全的?
1)死循环:jdk1.7时,在多线程的情况下使用头插法,导致链表指针顺序被修改,出现环形链表,形成死循环(jdk1.8之后采用尾插法避免了链表倒置)
2)数据覆盖:哈希冲突时,元素被覆盖
3)数据丢失:扩容的时候,会出现部分数据未被正常迁移的情况

2.4 数组和集合的区别
  1. 长度:数组长度固定;集合可以动态扩容
  2. 元素:数组可以包含基本数据类型和对象;集合只能包含对象
  3. 方法:数组是能通过下标访问数据;集合提供了丰富的API方法,如add()、remove()
2.5 ArrayList和LinkedList的区别
  • 底层数据结构:ArrayList的底层的数据结构是数组;LinkedList的数据结构是链表;
  • 访问速度:ArrayList访问一个元素的时间复杂度为O(1);LinkedList访问一个元素的时间复杂度为O(n)
  • 插入和删除速度:ArrayList插入和删除元素的时间复杂度为O(n);LinkedList插入和删除元素的时间复杂度为为O(1)
  • 空间占用:ArrayList会预分配一段连续的空间,占用空间比较大;LinkedList插入的时候才会分配空间,占用空间比较小
2.6 ArrayList底层原理

底层数据结构:数组

底层原理

  1. 空参创建集合时,在底层创建一个长度为0的数组

  2. 添加第一个元素时,创建一个长度为10的数组

  3. 添加过程:

    1)判断是否存满,满则扩容为原来的1.5
    扩容机制:
    创建:创建一个长度为原来1.5倍的新数组
    复制:将原数组复制到新数组
    更新引用:将原数组的引用指向新数组
    2)将size位置设置为待加入的值
    3)size+1

ArrayList是线程不安全的:
可能导致数组下标越界空值数组大小和实际插入大小不一致,可以使用CopyOnWriteArrayList、Vector代替,使之变成线程安全的。

2.7 LinkedList底层原理

底层数据结构:链表

因为长度不是固定的,所以不需要考虑扩容的情况。

2.8 CopyOnWriteArrayList底层原理

如何保证线程安全?

  • volatile关键字修饰数组,保证对数组的修改对其他线程是可见的
  • 写操作(加锁ReentrantLock):创建一个新数组,将原数组拷贝到新数组,并将原数组的引用指向新数组;
    读操作(不加锁):读取原数组;
2.9 HashSet底层原理

底层数据结构:哈希表(数组+链表+红黑树)

底层原理

  1. 空参创建时,底层创建一个长度为0的数组
  2. 添加首个元素时,创建一个长度为16的数组,默认加载因子是0.75(即达到数组的0.75倍,会进行扩容)
  3. 添加过程:
    1)调用hashCode()根据属性值计算哈希值;
    2)再根据(n-1)&hash计算桶的位置;
    3)如果位置为空,直接存入;如果位置不为空,调用equals()方法比较属性值,一样不存入,不一样,挂在下面,形成链表
    4)如果链表长度>8 & 数组长度>=64,自动转换为红黑树;如果树中节点<6,自动退化为链表
    5)判断元素个数是否超过了数组大小*加载因子,超过则扩容
    扩容机制:
    创建:创建一个长度为原来2倍的新数组
    复制:将原数组的键值对重新哈希分配到新数组中
    更新引用:将原数组的引用指向新数组

LinkedHashSet:底层数据结构是哈希表+双向链表,使用双向链表维护了插入顺序或访问顺序

2.10 HashMap底层原理

底层数据结构:哈希表(数组+链表+红黑树)

底层原理

  1. 空参创建时,底层创建一个长度为0的数组
  2. 添加首个元素时,创建一个长度为16的数组,默认加载因子是0.75(即达到数组的0.75倍,会进行扩容)
  3. 添加过程:
    1)调用hashCode()根据属性值计算哈希值;
    2)再根据(n-1)&hash计算桶的位置;
    3)如果位置为空,直接存入;如果位置不为空,调用equals()方法比较属性值,一样则覆盖,不一样,挂在下面,形成链表
    4)如果链表长度>8 & 数组长度>=64,自动转换为红黑树;如果树中节点<6,自动退化为链表
    5)判断负载因子是否超过阈值,超过则扩容
    扩容机制:
    创建:创建一个长度为原来2倍的新数组
    复制:将原数组的键值对重新分配到新数组中
    更新引用:将原数组的引用指向新数组

1.HashMap的key可以是null?
可以是null键,但只能有一个。计算哈希值时,如果为null返回0

2.如何解决哈希冲突?
哈希冲突:不同属性值计算出的哈希值相同
使用拉链法解决哈希冲突
jdk1.8之前:数组+链表
jdk1.8之后:数组+链表+红黑树

3.其他解决哈希冲突的方法?

  1. 拉链法:将哈希值相同的连接在同一个桶中
  2. 开发寻址法:在哈希表中寻找另一个可用的位置
  3. 再哈希法:使用另一个哈希函数计算哈希值,直到找到空槽
  4. 哈希表扩容:扩大哈希表,减少冲突的概率

4.为什么是扩大2倍(HashMap大小为什么总是2的n次方)?
因为这样在扩容的时候,只需要将原哈希值和len-1进行按位与,高位为0则索引不变,高位为1则新索引=原索引+旧数组容量,从而把哈希冲突的节点随机分配到了新数组中。

2.11 HashTable底层原理

内部的方法基本都经过synchronized的修饰,锁住的是整个哈希表,锁的粒度比较大,保证线程的安全

2.12 ConcurrentHashMap底层原理

jdk1.7:使用分段锁,用的是ReentrantLock,锁住的是一部分数据,当一个线程访问其中一段数据时,其他线程也能访问其他段
jdk1.8:使用CAS机制或synchronized,锁的是头结点,锁的粒度更细,提高了并发能力;put操作时,hash槽位为空,则使用CAS操作设置值,槽位不为空,使用synchronized申请锁,进行操作

为什么要同时使用CAS、synchronized?
结合锁竞争情况,选择不用的方法
1)竞争较弱:CAS是无锁的,通过硬件实现原子性,但当锁竞争激烈的时候,CAS操作会频繁失败,导致线程不断重试,降低性能。
2)竞争激烈:synchronized在锁竞争激烈的情况下,通过锁升级机制可以有效控制线程的竞争。

2.13 HashMap和HashTable的区别

线程安全:HashMap线程不安全;HashTable安全

效率:HashMap效率较高;HashTable,使用了锁,效率较低

底层数据结构:HashMap在jdk1.8之后底层的数据结构为数组+链表+红黑树,当链表长度>8 & 数组长度>=64,会转换为红黑树,如果数组长度<64,则扩容;HashTable则没有这种机制

对null键和null值的支持:HashMap支持null键和null值,但null键只能有一个;HashTable不支持null键和null值,会抛出空指针异常

初始容量大小和扩充容量大小:HashMap默认初始容量为16,扩容为2n,如果指定了容量大小,会扩充为2的幂次方;HashTable默认容量为11,扩容为2n+1,如果指定了容量大小,直接使用

哈希函数的实现:HashMap在计算哈希值时进行了高位和低位的混合扰动处理;HashTable则直接使用哈希值

2.14 HashTable和ConcurrentHashMap的区别

线程安全方式:HashTable是通过synchronized修饰方法保证线程安全;ConcurrentHashMap是通过CAS或synchronized保证线程安全的。

底层数据结构:HashTable的底层数据结构是数组+链表;ConcurrentHashMap的底层数据结构jdk7是分段数组+链表、jdk8是数组+链表+红黑树

2.15 HashMap、HashTable、ConcurrentHashMap的区别

线程安全:不安全;安全;安全

底层数据结构:数组+链表+红黑树;数组+链表;数组+链表+红黑树

对null键和null值的支持:不支持;支持;支持;

初始容量大小和扩充容量大小:16、2n ;11、2n+1;16、2n

哈希函数的实现:高低位混合扰动;直接使用哈希值;高低位混合扰动

相关文章:

【集合】底层原理实现及各集合之间的区别

文章目录 集合2.1 介绍一下集合2.2 集合遍历的方法2.3 线程安全的集合2.4 数组和集合的区别2.5 ArrayList和LinkedList的区别2.6 ArrayList底层原理2.7 LinkedList底层原理2.8 CopyOnWriteArrayList底层原理2.9 HashSet底层原理2.10 HashMap底层原理2.11 HashTable底层原理2.12…...

软考高级-系统架构设计师 论文范文参考(二)

文章目录 论企业应用集成论软件三层结构的设计论软件设计模式的应用论软件维护及软件可维护性论信息系统安全性设计论信息系统的安全性设计(二)论信息系统的架构设计论信息系统架构设计(二) 论企业应用集成 摘要: 2016年9月&#xff0c;我国某省移动通信有限公司决定启动VerisB…...

srp batch

参考网址&#xff1a; Unity MaterialPropertyBlock 正确用法&#xff08;解决无法合批等问题&#xff09;_unity_define_instanced_prop的变量无法srp合批-CSDN博客 URP | 基础CG和HLSL区别 - 哔哩哔哩 (bilibili.com) 【直播回放】Unity 批处理/GPU Instancing/SRP Batche…...

【Linux运维涉及的基础命令与排查方法大全】

文章目录 前言1、计算机网络常用端口2、Kali Linux中常用的命令3、Kali Linux工具的介绍4、Ubuntu没有网络连接解决方法5、获取路由6、数据库端口 前言 以下介绍计算机常见的端口已经对应的网络协议&#xff0c;Linux中常用命令&#xff0c;以及平时运维中使用的排查网络故障的…...

【2025最新Java八股】redis中io多路复用怎么回事,和多线程的关系

io多路复用 IO 多路复用和多线程是两种不同的技术&#xff0c;他们都是用于改善程序在处理多个任务或多个数据流时的效率和性能的。 但是他俩要解决的问题不一样&#xff01;IO多路复用主要是提升I/O操作的效率和利用率&#xff0c;所以适合 IO 密集型应用。多线程则是提升CP…...

Webview+Python:用HTML打造跨平台桌面应用的创新方案

目录 一、技术原理与优势分析 1.1 架构原理 1.2 核心优势 二、开发环境搭建 2.1 安装依赖 2.2 验证安装 三、核心功能开发 3.1 基础窗口管理 3.2 HTML↔Python通信 JavaScript调用Python Python调用JavaScript 四、高级功能实现 4.1 系统级集成 4.2 多窗口管理 五…...

Nginx HTTP 414 与“大面积”式洪水攻击联合防御实战

一、引言 在大规模分布式应用中&#xff0c;Nginx 常作为前端负载均衡和反向代理服务器。攻击者若结合超长 URI/头部攻击&#xff08;触发 HTTP 414&#xff09;与海量洪水攻击&#xff0c;可在网络层与应用层形成双重打击&#xff1a;一方面耗尽缓冲区和内存&#xff0c;另一…...

Oracle高级语法篇-集合操作

Oracle 集合操作详解 作为数据库领域的佼佼者&#xff0c;Oracle 提供了功能强大的集合操作符&#xff0c;它们能够合并多个查询的结果集&#xff0c;极大提升数据处理效率。接下来&#xff0c;本文将从基础知识点到实战案例&#xff0c;全方位剖析 Oracle 的集合操作。 一、…...

克服储能领域的数据处理瓶颈及AI拓展

对于储能研究人员来说&#xff0c;日常工作中经常围绕着一项核心但有时令人沮丧的任务&#xff1a;处理实验数据。从电池循环仪的嗡嗡声到包含电压和电流读数的大量电子表格&#xff0c;研究人员的大量时间都花在了提取有意义的见解上。长期以来&#xff0c;该领域一直受到对专…...

包含物体obj与相机camera的 代数几何代码解释

反余弦函数的值域在 [0, pi] 斜体样式 cam_pose self._cameras[hand_realsense].camera.get_model_matrix() # cam2world# 物体到相机的向量 obj_tcp_vec cam_pose[:3, 3] - self.obj_pose.p dist np.linalg.norm(obj_tcp_vec) # 物体位姿的旋转矩阵 obj_rot_mat self.ob…...

excel解析图片pdf附件不怕

背景 工作中肯定会有导入excel还附带图片附件的下面是我解析的excel&#xff0c;支持图片、pdf、压缩文件实现 依次去解析excel&#xff0c;看看也没有附件&#xff0c;返回的格式是Map&#xff0c;key是第几行&#xff0c;value是附件list附件格式都被解析成pdf格式Reader.jav…...

【Spring】依赖注入的方式:构造方法、setter注入、字段注入

在Spring框架中&#xff0c;除了构造器注入&#xff08;Constructor Injection&#xff09;和Setter注入&#xff08;Setter Injection&#xff09;&#xff0c;还有一种依赖注入方式&#xff1a;字段注入&#xff08;Field Injection&#xff09;。字段注入通过在Bean的字段上…...

mybatis实现增删改查1

文章目录 19.MyBatis查询单行数据MapperScan 结果映射配置核心文件Results自定义映射到实体的关系 多行数据查询-完整过程插入数据配置mybatis 控制台日志 更新数据删除数据小结通过id复用结果映射模板xml处理结果映射 19.MyBatis 数据库访问 MyBatis&#xff0c;MyBatis-Plus…...

Git,本地上传项目到github

一、Git的安装和下载 https://git-scm.com/ 进入官网&#xff0c;选择合适的版本下载 二、Github仓库创建 点击右上角New新建一个即可 三、本地项目上传 1、进入 要上传的项目目录&#xff0c;右键&#xff0c;选择Git Bash Here&#xff0c;进入终端Git 2、初始化临时仓库…...

基于flask+vue框架的灯饰安装维修系统u49cf(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,工单人员,服务项目,订单记录,服务记录,评价记录 开题报告内容 基于 FlaskVue 框架的灯饰安装维修系统开题报告 一、选题背景与意义 &#xff08;一&#xff09;选题背景 随着城市化进程的加速与居民生活品质的显著提升&#xf…...

【算法】BFS-解决FloodFill问题

目录 FloodFill问题 图像渲染 岛屿数量 岛屿的最大面积 被围绕的区域 FloodFill问题 FloodFill就是洪水灌溉的意思&#xff0c;假设有下面的一块田地&#xff0c;负数代表是凹地&#xff0c;正数代表是凸地&#xff0c;数字的大小表示凹或者凸的程度。现在下一场大雨&…...

GIS开发笔记(10)基于osgearth实现二三维地图的一键指北功能

一、实现效果 二、实现原理 获取视图及地图操作器,通过地图操作器来重新设置视点,以俯仰角 (0.0)和偏航角 (-90.0)来设置。 osgEarth::Util::Viewpoint(…) 这里创建了一个新的 Viewpoint 对象,表示一个特定的视角。构造函数的参数是: 第一个参数:是视角名称。 后面的 6 个…...

Spring Boot日志系统详解:Logback与SLF4J的默认集成

大家好呀&#xff01;&#x1f44b; 今天我们来聊聊Spring Boot中一个超级重要但又经常被忽视的功能——日志系统&#xff01; 一、日志系统的重要性 首先&#xff0c;咱们得明白为什么日志这么重要&#xff1f;&#x1f937;‍♂️ 想象一下&#xff0c;你正在玩一个超级复…...

【C++】Json-Rpc框架项目介绍(1)

项目介绍 RPC&#xff08;Remote Procedure Call&#xff09;即远程过程调用&#xff0c;是一种通过网络从远程计算机程序中请求服务而不需要了解底层网络实现细节的一种 协议 。 RPC&#xff08;Remote Procedure Call&#xff09;可以使用多种网络协议进行通信&#xff0c;如…...

Docker 部署 PostgreSQL 数据库

Docker 部署 PostgreSQL 数据库 基于 Docker 部署 PostgreSQL 数据库一、拉取 PostgreSQL 镜像二、运行 PostgreSQL 容器三、运行命令参数详解四、查看容器运行状态 基于 Docker 部署 PostgreSQL 数据库 一、拉取 PostgreSQL 镜像 首先&#xff0c;确保你的 Docker 环境已正确…...

用 Go 优雅地清理 HTML 并抵御 XSS——Bluemonday

1、背景与动机 只要你的服务接收并回显用户生成内容&#xff08;UGC&#xff09;——论坛帖子、评论、富文本邮件正文、Markdown 等——就必须考虑 XSS&#xff08;Cross‑Site Scripting&#xff09;攻击风险。浏览器在解析 HTML 时会执行脚本&#xff1b;如果不做清理&#…...

Python爬虫从入门到实战详细版教程

Python爬虫从入门到实战详细版教程 文章目录 Python爬虫从入门到实战详细版教程书籍大纲与内容概览第一部分:爬虫基础与核心技术1. 第1章:[爬虫概述](https://blog.csdn.net/qq_37360300/article/details/147431708?spm=1001.2014.3001.5501)2. 第2章:HTTP协议与Requests库…...

window上 elasticsearch v9.0 与 jmeter5.6.3版本 冲突,造成es 启动失败

[2025-04-22T11:00:22,508][ERROR][o.e.b.Elasticsearch ] [AIRUY] fatal exception while booting Elasticsearchjava.nio.file.NoSuchFileException: D:\Program Files\apache-jmeter-5.6.3\lib\logkit-2.0.jar 解决方案&#xff1a; 降低 es安装版本 &#xff0c;选择…...

【C++初阶】第15课—模版进阶

文章目录 1. 模版参数2. 模版的特化2.1 概念2.2 函数模版特化2.3 类模板特化2.3.1 全特化2.3.2 偏特化 3. 模版的分离和编译4. 总结 1. 模版参数 模版参数分为类型形参和非类型参数之前我们写过的大量代码&#xff0c;都是用模版定义类的参数类型&#xff0c;跟在class和typena…...

黑阈免激活版:智能管理后台,优化手机性能

在使用安卓手机的过程中&#xff0c;许多用户会遇到手机卡顿、电池续航不足等问题。这些问题通常是由于后台运行的应用程序过多&#xff0c;占用大量系统资源导致的。今天&#xff0c;我们要介绍的 黑阈免激活版&#xff0c;就是这样一款由南京简域网络科技工作室开发的手机辅助…...

C++17 新特性简解

C17 新特性简解 一、核心语言特性 1. 结构化绑定&#xff08;Structured Bindings&#xff09; 用途&#xff1a;解构复合类型&#xff08;如元组、结构体&#xff09;为独立变量 示例&#xff1a; #include <iostream> #include <tuple>int main() {// 解构 st…...

神经网络的 “成长密码”:正向传播与反向传播深度解析(四)

引言 在神经网络的神秘世界里&#xff0c;正向传播和反向传播是驱动模型学习和进化的核心机制。它们如同神经网络的 “左右脑”&#xff0c;正向传播负责信息的前向流动与初步处理&#xff0c;反向传播则通过优化权重参数来提升模型性能&#xff0c;二者相辅相成&#xff0c;共…...

Mujoco robosuite 机器人模型

import ctypes import os# 获取当前脚本所在的目录 script_dir os.path.dirname(os.path.abspath(__file__))# 构建库文件的相对路径 lib_relative_path os.path.join(dynamic_models, UR5e, Jb.so)# 拼接成完整的路径 lib_path os.path.join(script_dir, lib_relative_path…...

在Ubuntu 18.04下编译OpenJDK 11

在Ubuntu 18.04下编译OpenJDK 11 源码下载地址&#xff1a; 链接: https://pan.baidu.com/s/1QAdu-B6n9KqeBakGlpBS3Q 密码: 8lho Linux下的环境要求 不同版本的jdk会要求在不同版本的Ubuntu下编译&#xff0c;不要用太高版本的Ubuntu或者gcc&#xff0c;特别是gcc&#xf…...

K8s:概念、特点、核心组件与简单应用

一、引言 在当今云计算和容器技术蓬勃发展的时代&#xff0c;Kubernetes&#xff08;简称 K8s&#xff09;已成为容器编排领域的事实标准。它为管理容器化应用提供了高效、可靠的解决方案&#xff0c;极大地简化了应用的部署、扩展和运维过程。无论是小型初创公司还是大型企业…...