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

ArrayList、LinkedList、HashMap、HashTable、HashSet、TreeSet

集合族谱

在这些集合中,仅有vector和hashtable是线程安全的,其内部方法基本都有synchronized修饰。

ArrayList

底层采用Object数组实现,实现了RandomAccess接口因此支持随机访问。插入删除操作效率慢。

ArrayList需要一份连续的内存空间。

ArrayList扩容机制

ArrayList添加元素时,若达到了内部数组指定的数量上限,会自动进行扩容:

  1. 计算新容量,一般是原容量的1.5倍(1.5 倍,是因为 1.5 可以充分利用移位操作,减少浮点数或者运算时间和运算次数)
  2. 根据新容量创建新数组
  3. 把原来的数据拷贝到新数组中
  4. 更新ArrayList内部指向原数组的引用,指向新数组

ArrayList哪里不安全 

首先,对arraylist添加一个元素,分为3步

  • 判断数组是否需要扩容,如果需要就调用grow方法扩容;
  • 将数组的size位置设置值(因为数组的下标是从0开始的);
  • 将当前集合的大小+1

多线程插入删除下,ArrayList会暴露三个问题:

  • 出现null值:
    假设arraylist容量为10,线程1检查当前size=4,不需要扩容,于是在index=4进行插入,但是还没有size++,线程2又来进行插入,检查不需要扩容且size=4,于是也在index=4执行插入,然后两个线程同时执行size++,就导致实际size=6,两次插入都在index=4,而index=5的地方并没有插入数据。
  • 索引越界异常
    还是上述例子,假设线程1检查size=9,没有到10,无需扩容,于是在index=9的地方插入,但还没有size++,线程2来检查size=9,也在index=9的地方插入,然后两个线程同时++,导致size=11。
  • 集合的size()和实际add数量不符
    size++不是原子操作,分为三步,获取size值、size+1、新size覆盖老size,如果两个线程拿到一样的size同时覆盖,那么就导致有一次没加上。

为什么需要DEFAULTCAPACITY_EMPTY_ELEMENTDATA标识未初始化的状态

  • 确保第一次添加元素时数组的容量扩展
    ArrayList 在没有明确指定容量时,会使用 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 来标识该 ArrayList 尚未添加任何元素。这意味着,当创建一个空的 ArrayList 时,它的内部数组并不会立即分配实际的空间,而是会指向一个空的数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA)。这节省了内存开销。
    当第一次往这个 ArrayList 中添加元素时,内部数组的大小就需要扩展。因为 ArrayList 的默认初始容量是 10,所以一旦添加元素,数组就会重新分配一个合适的大小(10)来存放这些元素。
  • 避免不必要的内存分配
    如果没有 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 这种标识,ArrayList 每次创建时都会为空的实例分配一个固定大小的数组(比如长度为 10)占用内存。
    通过使用 DEFAULTCAPACITY_EMPTY_ELEMENTDATAArrayList 会在初始化时使用一个空数组来表示尚未使用的状态,只有在第一次添加元素时,才会根据需要分配和扩展内部数组。这样可以节省内存开销。
  • 区分“空的”实例和“已初始化但无元素”实例

    DEFAULTCAPACITY_EMPTY_ELEMENTDATA 有一个特别的用途,就是帮助 ArrayList 区分两种不同的状态:

    • 空实例:=ArrayList 没有被初始化为一个非空的数组,只是一个空的实例。ArrayList<Integer> list = new ArrayList<>();

    • 已初始化但无元素ArrayList 已经分配了一个默认大小的数组,但当前还没有元素。ArrayList<Integer> list = new ArrayList<>(10);

LinkedList

HashMap

  • HashMap可以存储null的key和value,但null作为键(key的哈希值为0)只能有一个,null作为值可以有多个。
  • JDK1.8之前hashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突),如果多个键映射到同一个槽位,它们会以链表的形式存储在同一个槽位上,因为链表的查询时间是O(n),所以冲突很严重,一个索引上的链表非常长,效率就很低了
  • JDK1.8后的HashMap在解决哈希冲突时有了较大的变化,当链表长度大于等于8并且数组长度大于等于64时,将链表转化为红黑树,以减少搜索时间O(logn),但是在数量较少时,即数量小于6时,会将红黑树变回链表。
  • HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。并且HashMap总是使用2的幂作为哈希表的大小。
  • 解决哈希冲突的方法:
    • 链接法:使用链表或其他数据结构来存储冲突的键值对,将它们链接在同一个哈希桶中。
    • 开放寻址法:在哈希表中找到另一个可用的位置来存储冲突的键值对,而不是存储在链表中。常见的开放寻址方法包括线性探测、二次探测和双重散列。
    • 再哈希法:当发生冲突时,使用另一个哈希函数再次计算键的哈希值,直到找到一个空槽来存诸键值对。
    • 哈希桶扩容:当哈希冲突过多时,可以动态地扩大哈希桶的数量,重新分配键值对,以减少冲突的概率。

 

HashMap哪里线程不安全

  • JDK1.7 HashMap采用数组+链表的数据结构,多线程背景下,HashMap 使用头插法插入元素,可能出现环形链表造成Entry链死循环,多线程同时执行 put 操作,可能造成前一个 key 被后一个 key 覆盖,存在和数据丢失问题
  • JDK1.8 HashMap采用数组+链表+红黑二叉树的数据结构,优化了1.7中数组扩容的方案,解决了Entry链死循环和数据丢失问题。但是多线程背景下,put方法存在数据覆盖的问题。
    • Entry死循环:扩容时存在在原数组和新数组之间的指针切换,如果没有正确地处理链表节点的 next 指针,就可能导致某些节点指向自己,从而形成死循环。
    • 数据丢失:扩容时HashMap 的元素会被重新散列并放入新的数组桶中。如果在处理过程中存在指针的错误(例如链表 next 指针没有正确地更新),就可能发生丢失元素的情况,某些元素可能无法在新的数组中找到正确的位置,导致这些元素丢失。
  • 如果要保证线程安全,可以通过这些方法来保证:
    多线程环境可以使用Collections.synchronizedMap( )同步加锁的方式,但是这种方法是全局锁synchronized,很影响性能,不如使用ConurrentHashMap的分段锁,更适合高并发场景使用。

HashMap的put流程(jdk8)

  1. 根据要添加的键的哈希码(hashcode方法)得出在数组中的位置,即找到桶
  2. 检查该位置是否为空(即没有键值对存在)
  3. 若该位置已经存在其他键值对,检查该位置的键值对的键(equals方法)是否与要添加的键值对相同,若相同则新值覆盖旧值。put结束
  4. 若和键值对的键不相同,则再遍历链表或红黑树(遍历桶)来查找是否有相同的键和值,若有,则新值覆盖旧值,若无,则新值加到链表或红黑树中。
  5. 检查链表长度是否达到8且HashMap的数组长度是否大于等于64,是则将链表转换为红黑树。
  6. 检查负载因子是否超过阈值(默认为0.75),若键值对的数量(size)与数组长度(初始16)的比值大于阈值,则需要进行扩容操作。
  7. 扩容操作:创建一个新的两倍大小的数组。将旧数组中的键值对重新计算哈希码并分配到新数组中的位置。更新HashMap的数组引用和阈值参数。

HashMap的get方法哪里不安全

  • 空指针异常:在HashMap没有被初始化时如果尝试用null作为键调用get方法会抛出空指针异常。如果HashMap已经初始化,使用null作为键是允许的,因为HashMap支持null键。
  • 线程安全:HashMap本身不是线程安全的。例如在一个线程中调用get方法而另一个线程同时增加或删除元素,可能会导致读取操作得到错误的结果或抛出ConcurrentModificationException。如果需要在多线程环境中使用类以HashMap的数据结构,可以考虑使用ConcurrentHashMap。

HashMap为啥用String作为key

String对象是不可变的,一旦创建就不能被修改,这确保了Key的稳定性。如果Key是可变的,可能会导致hashCode和equals方法的不一致,进而影响HashMap的正确性。

为什么HashMap要用红黑树而不是平衡二叉树

平衡二叉树追求的是一种“完全平衡”状态:任何结点的左右子树的高度差不会超过1,优势是树的结点是很平均分配的,导致每次进行插入/删除节点的时候,几乎都会破坏平衡树的第二个规则,进而需要通过左旋和右旋来进行调整,使之再次成为一颗符合要求的平衡树。
红黑树不追求这种完全平衡状态,而是追求一种“弱平衡”状态:整个树最长路径不会超过最短路径的2倍,不会像平衡二叉树一样频繁左右旋,也就是栖牲了一部分查找的性能效率来换取一部分维持树平衡状态的成本

HashTable

内部方法基本都经过synchronized修饰,不可以有null的key和value。默认初始容量为11,每次扩容变为原来的2n+1。创建时给定了初始容量,会直接用给定的大小。底层数据结构为数组+链表。它基本被淘汰了,要保证线程安全可以用ConcurrentHashMap。

HashSet

底层由HashMap实现,key就是hashset的值,所有的key的value相同,是一个名为PRESENT的Object类型常量。

LinkedHashSet

底层由LinkedHashMap实现,继承了HashSet类,使用双向链表维护元素插入顺序。

TreeSet 

底层由TreeMap实现(红黑树),添加元素到集合时按照比较规则将其插入合适的位置,保证插入后的集合仍然有序(自然顺序,比如插入1,3,2,输出出来是1,2,3)

相关文章:

ArrayList、LinkedList、HashMap、HashTable、HashSet、TreeSet

集合族谱 在这些集合中&#xff0c;仅有vector和hashtable是线程安全的&#xff0c;其内部方法基本都有synchronized修饰。 ArrayList 底层采用Object数组实现&#xff0c;实现了RandomAccess接口因此支持随机访问。插入删除操作效率慢。 ArrayList需要一份连续的内存空间。 A…...

手动配置IP

手动配置IP&#xff0c;需要考虑四个配置项&#xff1a; 四个配置项 IP地址、子网掩码、默认网关、DNS服务器 IP地址&#xff1a;格式表现为点分十进制&#xff0c;如192.168.254.1 子网掩码&#xff1a;用于区分网络位和主机位 【子网掩码的二进制表达式一定是连续的&#…...

idea如何使用AI编程提升效率-在IntelliJ IDEA 中安装 GitHub Copilot 插件的步骤-卓伊凡

idea如何使用AI编程提升效率-在IntelliJ IDEA 中安装 GitHub Copilot 插件的步骤-卓伊凡 问题 idea编译器 安装copilot AI工具 实际操作 在 IntelliJ IDEA 中安装 GitHub Copilot 插件的步骤如下&#xff1a; 打开 IntelliJ IDEA&#xff1a; 打开你的 IntelliJ IDEA 应用…...

游戏引擎学习第101天

回顾当前情况 昨天的进度基本上完成了所有内容&#xff0c;但我们还没有进行调试。虽然我们在运行时做的事情大致上是对的&#xff0c;但还是存在一些可能或者确定的bug。正如昨天最后提到的&#xff0c;既然现在时间晚了&#xff0c;就不太适合开始调试&#xff0c;所以今天我…...

css块级元素和行内元素区别

在CSS中&#xff0c;元素可以分为两大类&#xff1a;块级元素&#xff08;Block-level elements&#xff09;和行内元素&#xff08;Inline elements&#xff09;。这两种元素在网页布局中起着不同的作用&#xff0c;主要体现在它们的显示方式、尺寸控制、以及与其他元素的交互…...

JAVA安全—Shiro反序列化DNS利用链CC利用链AES动态调试

前言 讲了FastJson反序列化的原理和利用链&#xff0c;今天讲一下Shiro的反序列化利用&#xff0c;这个也是目前比较热门的。 原生态反序列化 我们先来复习一下原生态的反序列化&#xff0c;之前也是讲过的&#xff0c;打开我们写过的serialization_demo。代码也很简单&…...

什么是信息熵

信息熵 公式 一个离散随机变量 X X X的可能取值为 X x 1 , x 2 , . . . , x n Xx_1,x_2,...,x_n Xx1​,x2​,...,xn​&#xff0c;而对应的概率为 p i p ( X x i ) p_ip(Xx_i) pi​p(Xxi​),如下 x 1 x_1 x1​ x 2 x_2 x2​ x 3 x_3 x3​ x 4 x_4 x4​… x n x_n xn​p( x …...

使用API有效率地管理Dynadot域名,清除某一文件夹中域名的默认DNS设置

关于Dynadot Dynadot是通过ICANN认证的域名注册商&#xff0c;自2002年成立以来&#xff0c;服务于全球108个国家和地区的客户&#xff0c;为数以万计的客户提供简洁&#xff0c;优惠&#xff0c;安全的域名注册以及管理服务。 Dynadot平台操作教程索引&#xff08;包括域名邮…...

2.11 sqlite3数据库【数据库的相关操作指令、函数】

练习&#xff1a; 将 epoll 服务器 客户端拿来用 客户端&#xff1a;写一个界面&#xff0c;里面有注册登录 服务器&#xff1a;处理注册和登录逻辑&#xff0c;注册的话将注册的账号密码写入数据库&#xff0c;登录的话查询数据库中是否存在账号&#xff0c;并验证密码是否正确…...

当 LSTM 遇上 ARIMA!!

大家好&#xff0c;我是小青 ARIMA 和 LSTM 是两种常用于时间序列预测的模型&#xff0c;各有优劣。 ARIMA 擅长捕捉线性关系&#xff0c;而 LSTM 擅长处理非线性和长时间依赖的关系。将ARIMA 和 LSTM 融合&#xff0c;可以充分发挥它们各自的优势&#xff0c;构建更强大的时…...

kali连接xshell

1.先保证宿主机&#xff1a;以太网适配器 VMware Network Adapter VMnet8 和kali&#xff08;net 模式&#xff09;在同一个网段 windows VMnet8开启 查看是否是自动获取ip ipv4 和ipv6一样的 查看 windows VMnet8的IPv4的地址 查看 kali 的IP地址 window ping的结果&#xf…...

图像曲率滤波

看到这么一个非常有意思的东西&#xff0c;记录一下 https://www.zhihu.com/question/35499791 https://zhuanlan.zhihu.com/p/22971865 GCFilter_talk.pdf_免费高速下载|百度网盘-分享无限制 https://github.com/YuanhaoGong/CurvatureFilter?tabreadme-ov-file...

TCP 和 UDP 可以绑定相同的端口吗?

前言 当一个网络接口接收到一个数据报时&#xff0c;IP 模块首先检查目的地址是否为自己的 IP 地址&#xff0c;如果是的话&#xff0c;数据报交付给由 IPv4 头部的协议字段指定的协议模块。 TCP 和 UDP 在内核中是两个完全独立的模块&#xff0c;送给 TCP/UDP 模块的报文根据…...

【Python网络爬虫】爬取网站图片实战

【Python网络爬虫】爬取网站图片实战 Scrapying Images on Website in Action By Jackson@ML *声明:本文简要介绍如何利用Python爬取网站数据图片,仅供学习交流。如涉及敏感图片或者违禁事项,请注意规避;笔者不承担相关责任。 1. 创建Python项目 1) 获取和安装最新版…...

2024年博客之星年度评选—创作影响力评审+主题文章创作评审目前排名(2024博客之星陪跑小分队助力2024博客之星创作者成长)

2024年博客之星年度评选—创作影响力评审主题文章创作评审目前排名 2024年博客之星主题文章创作评审文章得分公布&#xff01;2024年博客之星创作影响力评审2024年博客之星主题文章创作评审目前排名公布&#xff01; 【2024博客之星】恭喜完成✅主题创作的226位博主&#xff0…...

【CLIP系列】4:目标检测(ViLD、GLIP)

目录 1 ViLD2 GLIP2.1 前言2.2 损失计算2.3 模型框架 1 ViLD OPEN-VOCABULARY OBJECT DETECTION VIA VISION AND LANGUAGE KNOWLEDGE DISTILLATION 从标题就能看出来&#xff0c;作者是把CLIP模型当成一个Teacher&#xff0c;去蒸馏他自己的网络&#xff0c;从而能Zero Shot去…...

Qt Designer菜鸟使用教程(实现一个本地英文翻译软件)

1 安装Qt Designer 安装这个包的时候会自带安装 Qt Designer, 安装目录为python的安装根目录的 Lib/site-packages/qt5_applications/Qt/bin 目录下。 pip install pyqt5-tools2 新建窗体 2.1 新建主窗体 创建之后如下图&#xff1a; 设置主窗口大小&#xff1a; 设置窗…...

【一文读懂】HTTP与Websocket协议

HTTP协议 概述 HTTP (Hypertext Transfer Protocol)&#xff0c;即超文本传输协议&#xff0c;是一种用于在客户端和服务器之间传输超文本&#xff08;例如网页、图片、音频、视频等&#xff09;的通信协议。它是万维网&#xff08;WWW&#xff09;的基础&#xff0c;负责在浏…...

大语言模型入门

大语言模型入门 1 大语言模型步骤1.1 pre-training 预训练1.1.1 从网上爬数据1.1.2 tokenization1.1.2.1 tokenization using byte pair encoding 1.3 预训练1.3.1 context1.3.2 training1.3.3 输出 1.2 post-training1.2.1 token 1.2 SFT监督微调1.3 人类反馈强化学习1.3.1 人…...

SQL 大厂面试题目(由浅入深)

今天给大家带来一份大厂SQL面试覆盖&#xff1a;基础语法 → 复杂查询 → 性能优化 → 架构设计&#xff0c;大家需深入理解执行原理并熟悉实际业务场景的解决方案。 1. 基础查询与过滤 题目&#xff1a;查询 employees 表中所有薪资&#xff08;salary&#xff09;大于 10000…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

破解路内监管盲区:免布线低位视频桩重塑停车管理新标准

城市路内停车管理常因行道树遮挡、高位设备盲区等问题&#xff0c;导致车牌识别率低、逃费率高&#xff0c;传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法&#xff0c;正成为破局关键。该设备安装于车位侧方0.5-0.7米高度&#xff0c;直接规避树枝遮…...

uniapp 小程序 学习(一)

利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 &#xff1a;开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置&#xff0c;将微信开发者工具放入到Hbuilder中&#xff0c; 打开后出现 如下 bug 解…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现指南针功能

指南针功能是许多位置服务应用的基础功能之一。下面我将详细介绍如何在HarmonyOS 5中使用DevEco Studio实现指南针功能。 1. 开发环境准备 确保已安装DevEco Studio 3.1或更高版本确保项目使用的是HarmonyOS 5.0 SDK在项目的module.json5中配置必要的权限 2. 权限配置 在mo…...

LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》

&#x1f9e0; LangChain 中 TextSplitter 的使用详解&#xff1a;从基础到进阶&#xff08;附代码&#xff09; 一、前言 在处理大规模文本数据时&#xff0c;特别是在构建知识库或进行大模型训练与推理时&#xff0c;文本切分&#xff08;Text Splitting&#xff09; 是一个…...

JS红宝书笔记 - 3.3 变量

要定义变量&#xff0c;可以使用var操作符&#xff0c;后跟变量名 ES实现变量初始化&#xff0c;因此可以同时定义变量并设置它的值 使用var操作符定义的变量会成为包含它的函数的局部变量。 在函数内定义变量时省略var操作符&#xff0c;可以创建一个全局变量 如果需要定义…...