ArrayList源码学习笔记(3)
时隔两年,重新读ArrayList源码,轻松了很多,以问题的方式记录一下收获
- 装饰器模式
 注释中提到ArrayList本身不是线程安全的,注释如下:
 * <p><strong>Note that this implementation is not synchronized.</strong>* If multiple threads access an <tt>ArrayList</tt> instance concurrently,* and at least one of the threads modifies the list structurally, it* <i>must</i> be synchronized externally.  (A structural modification is* any operation that adds or deletes one or more elements, or explicitly* resizes the backing array; merely setting the value of an element is not* a structural modification.)  This is typically accomplished by* synchronizing on some object that naturally encapsulates the list.** If no such object exists, the list should be "wrapped" using the* {@link Collections#synchronizedList Collections.synchronizedList}* method.  This is best done at creation time, to prevent accidental* unsynchronized access to the list:<pre>*   List list = Collections.synchronizedList(new ArrayList(...));</pre>
如果要想做到线程安全,需要对某个对象加锁的方式来实现,实现应当如下
synchronized(ojb) {list.add(item);
}
如果没有这么做,可以使用Collections.synchronizedList对ArrayList包装,并且最好是在一开始定义list的时候就进行包装,避免有的地方使用了未包装的原始list,代码如下:
 List list = Collections.synchronizedList(new ArrayList(...));
- 类签名里既然继承了AbstractList,为什么还要写implements List
public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
应该是作者写错了,后来没改回来只是觉得没有必要且保持和旧版本的一致了。参考博客 以及 stackOverFlow问答
- DEFAULTCAPACITY_EMPTY_ELEMENTDATA
 这是在无参构造函数使用的存储数据,默认不分配数组且空数组也复用,这内存节省到极致了,值得学习。
 	/*** Shared empty array instance used for default sized empty instances. We* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when* first element is added.*/private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};/*** Constructs an empty list with an initial capacity of ten.*/public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}
- 存储结构:Object[] elementData
 为什么使用Object?这个是java对于泛型的使用上有一些约束。如果直接创建T[]数组,会报错,因为编译器会进行类型擦除,并不能知道这个T类型是什么。所以干脆创建Object[]数组。(这个参考自 ArrayList 解析)
 鉴于泛型擦除,list只能做编译期的类型校验,运行时是无法校验的,除非有类型强转。
	public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);List list1 = list;list1.add("xx");System.out.println(list);}
输出:[1, xx]
- elementData前的transient关键字
 意思是序列化时忽略,writeObject和readObject单独实现。这两个方法必须声明为private,在java.io.ObjectStreamClass#getPrivateMethod()方法中通过反射获取到writeObject()这个方法。
 elementData定义为transient的优势:自己根据size序列化真实的元素,而不是根据数组的长度序列化元素,减少了空间占用。
- ensureExplicitCapacity直接进行了modCount++,我觉得不妥
 源码如下,其实下面的if语句为false的时候,grow不会执行,也就不会对list进行修改,所以modCount理论上不应该增加。
 结合add和remove方法来看,这么写是因为add和remove方法会调用ensureExplicitCapacity,所以将modCount++的动作下沉了。
 但是public方法ensureCapacity,也调用了ensureExplicitCapacity,而不一定会产生结构修改,除非size需要调整。所以这里的语义不太合理了。
	/*** 对外提供的方法,可以通过调用这个方法,在要写入大批数据之前进行容量保障,避免出现频繁扩容**/public void ensureCapacity(int minCapacity) {int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)// any size if not default element table? 0// larger than default for default empty table. It's already// supposed to be at default size.: DEFAULT_CAPACITY;if (minCapacity > minExpand) {ensureExplicitCapacity(minCapacity);}}/*** 内部私有实现**/private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);}
- MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
 最大大小设置的是int最大值-8,一堆讨论为什么要减8的以及实际上容量也能设置为int最大值的,这个暂时跳过。
 想到了另外一个问题,size的返回结果是int,那超出int怎么办?为什么不能设置为long?不能设置为long的原因很好理解,因为没必要,一般不会这么大,而且用long就会占用更多内存。那么超出int怎么办?没找到方法,或许自行设计一个复杂结构
- 扩容是newCapacity = oldCapacity + (oldCapacity >> 1),每次扩50%
- rangeCheckForAdd
 针对add和addAll方法会多校验一下index<0,为什么remove不需要校验呢?不太理解
- 代码风格:a方法调用了b方法,b方法写在a方法后面,更容易阅读。
- fastRemove方法
 去掉了index校验,只供内部使用,真的是太细了。对性能压榨到了极致
	/*** 公共方法,删除指定index的元素,有range校验**/public E remove(int index) {rangeCheck(index);modCount++;E oldValue = elementData(index);int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // clear to let GC do its workreturn oldValue;}/*** 公共方法,删除指定对象,在查找到对象之后,获取其index,通过调用fastRemove进行删除**/public boolean remove(Object o) {if (o == null) {for (int index = 0; index < size; index++)if (elementData[index] == null) {fastRemove(index);return true;}} else {for (int index = 0; index < size; index++)if (o.equals(elementData[index])) {fastRemove(index);return true;}}return false;}/*** 私有方法,删除指定index的元素,只供内部使用,因此没有做range校验**/private void fastRemove(int index) {modCount++;int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // clear to let GC do its work}
- batchRemove用了读写双指针来实现数据删除过程中的就地挪动
 有时候做算法题就会看到一些双指针解决的问题,jdk源码里就有相应实现
	/*** 删除给定集合的元素,它调用了batchRemove方法**/public boolean removeAll(Collection<?> c) {Objects.requireNonNull(c);return batchRemove(c, false);}/*** 保留给定集合的元素,它调用了batchRemove方法**/public boolean retainAll(Collection<?> c) {Objects.requireNonNull(c);return batchRemove(c, true);}/*** 批量删除方法,可以通过complement来控制传入的集合中的原始是需要删除还是保留* r、w双指针实现就地修改**/private boolean batchRemove(Collection<?> c, boolean complement) {final Object[] elementData = this.elementData;int r = 0, w = 0;boolean modified = false;try {for (; r < size; r++)if (c.contains(elementData[r]) == complement)elementData[w++] = elementData[r];} finally {// Preserve behavioral compatibility with AbstractCollection,// even if c.contains() throws.if (r != size) {System.arraycopy(elementData, r,elementData, w,size - r);w += size - r;}if (w != size) {// clear to let GC do its workfor (int i = w; i < size; i++)elementData[i] = null;modCount += size - w;size = w;modified = true;}}return modified;}
- modcount,是一个体系化的事情,是保证遍历的快速失败。需要保证每个影响正确性的地方都修改到,那么怎么保证呢?根据注释来看,是所有会导致list产生结构性变化的地方都需要修改modcount。然后确定方法中是否需要修改modCount就有根据了。
相关文章:
ArrayList源码学习笔记(3)
时隔两年,重新读ArrayList源码,轻松了很多,以问题的方式记录一下收获 装饰器模式 注释中提到ArrayList本身不是线程安全的,注释如下: * <p><strong>Note that this implementation is not synchronized.&…...
flutter怎么对ReorderableListView中的用于排序的控制手柄进行显示或隐藏
我在使用ReorderableListView创建可排序列表的时候,需要在编辑的时候才显示右侧的控制排序的手柄。研究了半天,配合搜索引擎,才找到正确的方案。 答案很简单,就是在它的属性当中有一个叫做:buildDefaultDragHandles的…...
python 1200例——【9】斐波那契数列
文章目录 定义求解方法1. 递归方法2. 循环方法3. 动态规划方法4. 矩阵方法总结:定义 斐波那契数列(Fibonacci sequence)是一个在自然世界中经常出现的数学序列。它是由0和1开始,然后的每个数字都是前两个数字的和。因此,斐波那契数列的前几个数字是:0, 1, 1, 2, 3, 5, 8…...
 
JavaScript读写T5557卡源码
本示例使用发卡器: https://item.taobao.com/item.htm?spma1z10.5-c-s.w4002-21818769070.13.48ce6f89XlQ9Vf&id675212889085 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-t…...
 
【数据结构】LRU缓存的简单模拟实现(leetcode力扣146LRU缓存)
文章目录 一、定义二、LRU模拟实现二、代码实现 一、定义 LRU是Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法。 Cache的容量有限,因此当Cache的容量用完后,而又有新的内容需要添加进来时, 就…...
 
基于电商场景的高并发RocketMQ实战-Commitlog基于内存的高并发写入优化、基于JVM offheap的内存读写分离机制
🌈🌈🌈🌈🌈🌈🌈🌈 【11来了】文章导读地址:点击查看文章导读! 🍁🍁🍁🍁🍁🍁dz…...
 
工具系列:TensorFlow决策森林_(3)使用dtreeviz可视化
文章目录 介绍设置安装 TF-DF 和 dtreeviz导入库 可视化分类树加载、清洗和准备数据分割训练/测试集并训练模型训练一个随机森林分类器显示决策树检查叶节点统计信息决策树如何对实例进行分类特征空间划分 可视化回归树加载、清洗和准备数据分割训练/测试集并训练模型训练一个随…...
 
【算法学习】斐波那契数列模型-动态规划
前言 我在算法学习过程中,针对斐波那契数列模型的动态规划的例题进行了一个整理,并且根据标准且可靠一点的动态规划解题思路进行求解类似的动归问题,来达到学习和今后复习的必要。 所谓的斐波那契数列模型,即当前状态的值等于前两…...
 
ES的安装和RestClient的操作
目录 初识elasticsearch 什么是elasticsearch elasticsearch的发展 Lucene的优缺点 elasticsearch的优势 倒排索引 es与mysql的概念对比 文档 索引 概念对比 架构 安装es 安装kibana 安装ik分词器 分词器 安装ik分词器 ik分词器的拓展和停用词典 操作索引库…...
访问者模式(Visitor)
访问者模式(Visitor Pattern)是一种将算法与对象结构分离的行为型设计模式。这种模式主要用于对一个由许多不同类型的对象构成的复杂对象结构(如组合结构)进行操作,而不需要对这些对象的类进行修改。 访问者模式涉及以下几个角色: 访问者(Visitor):为每一个具体元素类…...
 
ATTCK红队评估一
一、环境搭建 主机 ip地址 win7外网服务器(两张网卡) 外网:192.168.92.135 内网:192.168.52.143 server2003域成员主机 内网:192.168.52.141 server2008域空主机 内网:192.168.52.138 kali攻击机 …...
 
W5500-EVB-Pico评估版介绍
文章目录 1 概述2 板载资源2.1 硬件规格2.2 硬件规格2.3 工作条件 3 参考资料3.2 原理图3.3 尺寸图 (单位 : mm)3.4 参考例程 4 硬件协议栈优势 1 概述 W5500-EVB-Pico是基于树莓派RP2040和完全硬连线TCP/IP控制器W5500的微控制器开发板-基本上与树莓派Pico板相同,但…...
 
单聊和群聊
TCP协议单聊 服务端: import java.awt.BorderLayout; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.Vec…...
Swift 检测 iCloud状态
Show me the code: func isICloudContainerAvailable() -> Bool {if let _ FileManager.default.ubiquityIdentityToken {return true} else {return false} }推荐一下刚上线的 App 熊猫小账本,里面有用到这篇博客讲的内容 熊猫小账本 一个简洁的记账 App&…...
使用Windi CSS(基于vue-cli)
1、先创建vue项目 利用脚手架vue-cli创建,根据需求设置vue版本、babel等,无特别要求直接用默认的vue2或vue3就行 vue create 项目名 2、运行vue项目,利用vue-cli安装Windi CSS 官网指导:Vue CLI 集成 | Windi CSS 我的经历&a…...
 
操作无法完成(错误 0x000006ba),Windows 11 PDF打印机无法使用解决办法
操作无法完成(错误 0x000006ba),Windows 11 PDF打印机无法使用解决办法 解决方式一 先重启一次电脑,看看是否可以解决问题。 解决方式二 重新启动 Printer Spooler 服务...
 
Settings中电池选项-Android13
Settings中电池选项-Android13 1、设置中界面2、电池计算2.1 充电时间计算2.1.1 BatteryUsageStats获取2.1.2 BatteryStatsImpl计算 2.2 电池剩余使用时间2.2.1 Estimate获取2.2.2 BatteryStatsImpl计算 3、电池信息来源4、命令模拟* 日志 [电池]Android 9.0 电池未充电与充电字…...
解密 Java ForEach 提前终止问题
目录 前言:场景复现分析与解决方案解决方案详解总结 前言: 你是否曾在使用 Java 8 的 forEach 迭代集合时遇到过提前终止循环的问题?在这篇博客中,我们将深入探讨这一问题,并提供多种解决方案。通过场景复现、分析源码…...
 
7_js_dom编程入门1
Objective(本课目标) 掌握获取页面元素的常用方法 掌握事件触发案例 能够区分innerText和innerHTML的区别 综合案例训练 1 DOM 介绍 1.1 什么是DOM 文档对象模型(Document Object Model,简称DOM),是 …...
 
使用 Elasticsearch 检测抄袭 (一)
作者:Priscilla Parodi 抄袭可以是直接的,涉及复制部分或全部内容,也可以是释义的,即通过更改一些单词或短语来重新表述作者的作品。 灵感和释义之间是有区别的。 即使你得出类似的结论,也可以阅读内容,获得…...
 
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
 
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
 
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
 
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
 
学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
 
人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
 
R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
