面试官:如何安全地使用List

只会“有序无序”?面试官嫌弃的List、Set、Map回答! 讲到这里,小林的眼睛亮了起来:“原来它们的实现原理和使用场景才是关键!我补充道:“不仅如此,你还可以结合场景深入分析,比如:”1、为什么HashSet的查询速度快?因为底层用HashMap实现,基于哈希表的查询复杂度接近O(1)。2、为什么TreeMap比HashMap慢?TreeMap基于红黑树实现,增删查复杂度是O(logN),而HashMap在理想情况下是O(1)。3、线程安全问题怎么解决? 阅读详情

今天我们来讨论一个JUC中的集合类CopyOnWriteArrayList。

为什么研究这个类

在很多应用场景中,对于集合的读操作的频率一定会远远大于写操作。由于读操作根本不会修改原有的数据,因此对于每次读取都进行加锁其实是一种资源浪费。我们应该允许多个线程同时访问List的内部数据,毕竟读取操作是线程安全的。

JDK中提供了CopyOnWriteArrayList类,简称COW。为了将读取的性能发挥到极致,CopyOnWriteArrayList读取是完全不用加锁的,并且更厉害的是:写入也不会阻塞读取操作。只有写入和写入之间需要进行同步等待。这样一来,读操作的性能就会大幅度提升。那它是怎么做的呢?来吧,让我们一起研究一下。

设计原理

CopyOnWriteArrayList底层实现是通过Object[]存储元素的,内部的可变操作(add,set 等方法)都是把数据copy到一个新数组里,对新数组进行操作,再把新数组赋值给原来的对象,从而达到修改目的。

这样做的好处是不修改原数组,所以写操作不会影响到读操作。

从 CopyOnWriteArrayList 的名字就能看出CopyOnWriteArrayList 是满足CopyOnWrite 的 ArrayList,所谓CopyOnWrite 也就是说:在计算机,如果你想要对一块内存进行修改时,我们不在原有内存块中进行写操作,而是将内存拷贝一份,在新的内存中进行写操作,写完之后呢,就将指向原来内存指针指向新的内存,原来的内存就可以被回收掉了。

定位

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
}

 

从类的继承关系来看

  1. 实现RandomAccess接口,说明可随机访问
  2. 实现Cloneable接口,说明可克隆
  3. 实现了List接口,说明是一个列表
  4. 实现Serializable接口,说明可序列化

接下来让我们研究一下crud。

增

public boolean add(E e);// 新增元素,放在数组尾部
public void add(int index, E element);// 新增元素,放在数组指定位置
public boolean addIfAbsent(E e);// 新增元素,如果存在则返回false,如果不存在则放入末尾返回true
public int addAllAbsent(Collection<? extends E> c);// 批量新增元素,将指定集合中尚未包含在此列表中的所有元素附加到此列表的末尾,返回添加的个数
public boolean addAll(Collection<? extends E> c);// 将指定集合中的所有元素附加到此列表的末尾。
public boolean addAll(int index, Collection<? extends E> c);// 从指定位置开始,将当前位于该位置的元素(如果有)和任何后续元素向右移动(增加它们的索引)。新元素将按照指定集合的迭代器返回的顺序出现在此列表中。

 

此函数用于将指定元素添加到此列表的尾部,处理流程如下:

  • 获取锁(保证线程安全)
  • 根据Object数组复制一个长度为length+1的Object数组为newElements(此时,newElements[length]为null)
  • 将下标为length的数组元素newElements[length]设置为元素e,再设置当前Object[]为newElements,释放锁,返回。这样就完成了元素的添加。

删

public E remove(int index);// 移除指定位置的元素,有可能抛出数组越界异常
public boolean remove(Object o);// 移除对象,如果不存在则返回false,存在则移除后返回true
public boolean removeAll(Collection<?> c);// 批量移除指定集合元素,这是一个非常消耗内存的方法,因为内部会额外指定一个临时数组用来存放需要保留的元素,一共涉及4个数组(老数组、传入数组、临时数组、结果数组)
public boolean removeIf(Predicate<? super E> filter);// 和removeAll类似,内部实现需要有临时数组,也是代价昂贵的方法,请谨慎使用

指定位置删除的逻辑如下:

  • 获取锁
  • 获取数组,数组长度
  • 获取指定位置的元素(可能抛出数组越界异常)
  • 计算指定位置的元素是否是当前数组的最后一个
  • 如果是最后一个->不需要挪数据,只需要创建数组,copy数据到数组即可(少copy最后一个),设置数组并返回即可
  • 如果不是最后一个->创建数组,copy 0~index的数据到新数组,再copy (index+1)的数据到新数组,设置数组并返回即可

改

set方法,修改操作有可能数组越界,这一点需要注意。修改操作也是基于copy的,将数据copy到新数组,对新数组进行替换后再设置数组,从而达到set的目的。

查

public E get(int index);// 直接从数组中获取,可能抛出数组越界异常
public Spliterator<E> spliterator();
public Iterator<E> iterator();// 获取数组的迭代器,它的实现类是COWIterator,内部拥有一个快照的数组属性
public ListIterator<E> listIterator();// 获取listIterator迭代器,它的实现类是COWIterator,内部拥有一个快照的数组属性
public ListIterator<E> listIterator(int index);// 获取listIterator迭代器,index的作用是设置迭代器当前迭代的位置

先来看一个内部类COWIterator:

COWIterator表示一个迭代器,其也有一个Object类型的数组作为CopyOnWriteArrayList数组的快照,这种快照风格的迭代器方法在创建迭代器时使用了对当时数组状态的引用。此数组在迭代器的生存期内不会更改,因此不可能发生冲突,并且迭代器保证不会抛出 ConcurrentModificationException。在创建迭代器以后,迭代器就不会反映列表的添加、移除或者更改,因为在迭代器上进行的元素更改操作(remove、set 和 add)不受支持。这些方法将抛出 UnsupportedOperationException。

更深入的理解

CopyOnWriteArrayList每次写操作都会申请新内存空间,如果数据量较大的话,很容易触发young gc或者full gc,并且拷贝也会比较消耗内存,虽然适合读多写少的应用场景,在互联网应用中,数据量稍微有点多再操作add或set,非常容易引起故障,还是要谨慎使用。

再谈读,迭代读的时候是读取快照数据,只要生成了迭代器,迭代内的快照内容将保证不会发生改变,所以不适合用于实时读场景。

Java Set转List的4种方案:性能、内存与线程安全深度解析 Set转List是Java集合操作中最基础也最易被低估的类型转换场景,本质涉及Collection接口契约、JVM内存分配机制与泛型运行时行为。其核心原理在于Iterator遍历与数组拷贝策略的差异,不同实现直接影响GC压力、CPU缓存局部性及并发安全性。技术价值体现在避免OOM、保障高吞吐稳定性与构建不可变数据流;典型应用场景包括分页去重、第三方SDK参数适配、微服务DTO转换及响应式流处理。本文深入剖析new ArrayList<>(set)、Stream.collect、手动预扩容遍历及List.co 阅读详情

相关推荐

批处理脚本手动双击可以执行,但计划任务中执行失败

问题现象: 使用批处理脚本从linux端(samba server) 备份一些数据,设置计划任务每天凌晨两点定时备份。 1)、windows系统中映射共享文件夹为S:盘,需要输入共享用户名和密码 2)、脚本中使用xcopy命令复制共享目录中的文件到windows本地目录中。 脚本如下: set Name=%date:~0,4%%date:~5,2%%date:~8,2% chcp 65001 REM chcp防止中文乱码 mkdir "E:\脚本备份\bakscripts%Name%" xco

独孤清扬玩DB 4313

如何实现一个线程安全的list

线程安全,list

fighting_DW 3861

瑞芯微RK3588与FPGA的PCIe DMA通信性能优化实战

本文深入探讨了瑞芯微RK3588与FPGA之间PCIe DMA通信的性能优化实战。针对高速数据采集、AI推理等场景下的带宽瓶颈,文章从驱动配置、DMA描述符、中断处理、零拷贝技术及系统级调优等多个层面,提供了详细的优化策略与代码示例,旨在充分释放PCIe 3.0的理论带宽潜力,提升数据传输效率与稳定性。

sand8的博客 418

线程安全的List

线程安全的List 缘起 众所周知,ArrayList是线程不安全的。 那么我想要一个线程安全的集合要怎么办呢? 我们在基础阶段就有学过ArrayList和Vector的区别。但是你只说出Vector会让面试官鄙视的,除了Vector,你还会别的吗? 除了Vector,还有: java.util.Collections.SynchronizedList 它能把所有 List 接口的实现类转换成线程...

程序员大航子 1万+

如何构建智能聊天系统

聊天分为听、思考、读,简单的通过ASR、LLM、TTS三类模型的组合可以实现,最近openai推出支持多模态的GPT-4o模型,可以把三个模型真正融合成在一起。现在市面上的模型百花齐放,各有所长。要实现可落地的方案,需要结合业务,验证模型效果,同时也要考虑方案的可行性,有些模型在国内能用,有些在国内不能用。

xsgnzb的专栏 2109

手把手教你用mitmproxy抓包抖音数据流(附Python脚本模板)

本文详细介绍了如何使用mitmproxy抓包抖音数据流,包括环境搭建、证书安装和Python脚本模板。从mitmproxy配置到抖音API解析,提供完整的移动端数据流分析方案,帮助开发者快速掌握数据抓取技术,适用于应用优化、竞品分析和安全审计等场景。

weixin_33670786的博客 308

面试官问我List接口,我

忍不住了

3y 3561

Windows7 64位系统PL2303驱动无法安装问题解决

问题描述:电脑安装的是Win7 64位的旗舰版,USB to RS232这种线很简单,对于笔记本如果要用串口,就不能少了它了。USB to RS232转换芯片种类繁多,常见的有ch340,0108,PL2303,FT232等,这里面FT232的应该价格是相对比较贵的,因为FTDI的芯片本身比较贵。由于买了一根PL2303芯片的USB to RS232转换线,于是插上后发现驱动无法安装,又是在设备管

suxiang198的博客 5810

面试官问线程安全的List,看完再也不怕了!

最近在Java技术栈知识星球里面有球友问到了线程安全的 List:扫码查看答案或加入知识星球下面给大家总结一下吧,栈长在之前的文章《出场率比较高的一道多线程安全面试题》里...

Java技术栈,分享最主流的Java技术 2555

BRepNet与图神经网络融合:提升3D CAD加工特征识别的精度与泛化能力

本文探讨了BRepNet与图神经网络(GNN)融合技术如何显著提升3D CAD加工特征识别的精度与泛化能力。通过将CAD模型视为图结构,利用GNN的消息传递机制增强对复杂拓扑关系的理解,并结合BRepNet的局部特征提取,该融合方法在MFCAD++数据集上实现了高达99.53%的准确率,有效解决了传统方法在处理复杂相交特征时的局限性,为智能制造提供了关键技术支撑。

android的专栏 776

构建基于知识图谱的金融监管合规系统

金融行业是一个高度监管的领域,金融机构需要遵守众多复杂的法规和政策。传统的金融监管合规方式往往依赖于人工审查和简单的规则引擎,效率低下且容易出错。构建基于知识图谱的金融监管合规系统的目的在于利用知识图谱强大的知识表示和推理能力,实现对金融业务数据的自动化分析和合规检查,提高监管合规的效率和准确性。本系统的范围涵盖了金融机构的各类业务,如银行业务、证券业务、保险业务等。

AI天才研究院 1168

如何在List中放部分实体_面试官问线程安全的List,看完再也不怕了

最近在Java技术栈知识星球里面有球友问到了线程安全的 List:扫码查看答案或加入知识星球栈长在之前的文章《出场率比较高的一道多线程安全面试题》里面讲过 ArrayList 的不安全性。那么面试官会问你,既然 ArrayList 是线程不安全的,怎么保证它的线程安全性呢?或者有什么替代方案?往下看,看我如何碾压他!大部分人会脱口而出:用Vector,这样只会让面试官鄙视!除了Vector,你还会...

weixin_39644377的博客 275

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

java list 复制_面试官问线程安全的List,看完再也不怕了!

么面试官会问你,既然 ArrayList 是线程不安全的,怎么保证它的线程安全性呢?或者有什么替代方案?往下看,看我如何碾压他!大部分人会脱口而出:用Vector,这样只会让面试官鄙视!除了Vector,你还会别的吗?你至少还得说得上这种:java.util.Collections.SynchronizedList它能把所有 List 接口的实现类转换成线程安全的List,比 Vector 有更好...

weixin_39875842的博客 115

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

list stream.foreach 顺序会变吗_面试官问线程安全的List,看完再也不怕了!

么面试官会问你,既然 ArrayList 是线程不安全的,怎么保证它的线程安全性呢?或者有什么替代方案?往下看,看我如何碾压他!大部分人会脱口而出:用Vector,这样只会让面试官鄙视!除了Vector,你还会别的吗?你至少还得说得上这种:java.util.Collections.SynchronizedList它能把所有 List 接口的实现类转换成线程安全的List,比 Vector 有更好...

weixin_36051235的博客 2037

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

面试官:要不今天来讲讲Java的List吧,你对List了解多少?

面试官:要不今天来讲讲Java的List吧,你对List了解多少? 候选者:List在Java里边是一个接口,常见的实现类有ArrayList和LinkedList,在开发中用得最多的是ArrayList 候选者:ArrayList的底层数据结构是数组,LinkedList底层数据结构是链表。 面试官:那Java本身就有数组了,为什么要用ArrayList呢? 候选者:原生的数组会有一个特点:你在使用的时候必须要为它创建大小,而ArrayList不用。 候选者:在日常开发的时候,往往我们是不

分享Java技术知识,共同成长进步! 175

list 线程安全_面试官:你都工作3年了,怎么连ArrayList是线程不安全都没有搞清楚

作为一名java程序员,对ArrayList,相信再熟悉不过了。这个类我们平时接触得最多的一个列表集合类。面试时,也有不少面试官会针对此知识点考察求职者。小爱最近又去面试了,最近到某知名互联网公司面试,做了笔试题后,面试官刚好问ArrayList是线程安全还是非线程安全?小爱说是非线程安全,面试官问,你能说说为什么是非线程安全吗?小爱一时间说不出个所以然。面试官说:你都工作3年了,怎么连Array...

weixin_39553653的博客 252

java list 安全_Java面试经典合集1:如何安全地删除List中的元素

package com.chendan.mianshi;import java.util.ArrayList;import java.util.Iterator;import java.util.List;import java.util.stream.Collectors;public class MianShiTest1 {private static List list = new Arra...

weixin_39964869的博客 245

《对线面试官》 List集合 #14

面试官:要不今天来讲讲Java的List吧,你对List了解多少?候选者:List在Java里边是一个接口,常见的实现类有ArrayList和LinkedList,在开发中用得最多的是ArrayList候选者:ArrayList的底层数据结构是数组,LinkedList底层数据结构是链表。面试官:那Java本身就有数组了,为什么要用ArrayList呢?候选者:原生的数组会有一个特点:你在使用的时候必须要为它创建大小,而ArrayList不用。候选者:在日常开发的时候,往往我们是不知道数组的大小的候选者:如

惚如远行客的博客 124

你好,面试官 | 我用Java List 狂怼面试官~

我用再普通不过的 List 征服了京东面试官,哈哈

小龙coding的博客鸭~ 353

对线面试官 | 别再问我Java List八股文了

面试现场 面试官:你好,我是面试官xxx,请问你是大彬吗? 大彬:面试官,您好,我是大彬 面试官:现在方便面试吗? 大彬:嗯嗯,可以的 面试官:那我们现在开始面试吧 面试官:看你简历上写了熟悉集合相关内容,你了解Java的List吗? 大彬:嗯,List是一个接口,常见的实现类有ArrayList和LinkedList 面试官:讲讲这两个实现类的区别? 独白:老八股文了哈哈 大彬:ArrayList的底层数据结构是数组,支持下标访问,查询数据快。默认初始值大小为10,容量不足时会进行扩容

wdjnb的博客 450
上一篇: 常用类学习(String类和StringBuffer类详解)
下一篇: 8分钟,复习一遍红黑树!
java_lujj
博客等级 码龄4年 18粉丝 152原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值