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

ArrayList源码实现(一)

ArrayList源码实现(一)

1. ArrayList的大小是如何自动增加的?

  • 初始化
    • 在构造函数中,可以设定列表的初始值大小,如果没有的话默认使用,提供的静态数据
 public ArrayList(int initialCapacity) {if (initialCapacity > 0) {this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}}

注意:elementData 变量是 transient 关键字修饰的。

  • 动态扩容

重点关注 ensureCapacityInternal 函数,

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
实际的扩容函数
在这里插入图片描述

在这里插入图片描述

总结:在每次添加add 新的数据时,会进行一次 数组长度大小 的判断;

  1. 如果是数组为 空 时,最小长度就是 10,否则的话,带扩容的最小长度时 size(此时保存了多少数据)+1 (*calculateCapacity*
  2. 当最小容量 大于 数组的总长度时,进行扩容(ensureExplicitCapacity
  3. 数组的最大长度就是 Integer.*MAX_VALUE*
  4. 注意:扩容是创建了一个新的数组,将 elementData 指向了新的数组(elementData = Arrays.*copyOf*(elementData, newCapacity);

2. ArrayList的增加或者删除某个对象的对象为什么效率很低?

在ArraysList中 add、addAll与remove等方法,底层主要依赖于 System.*arraycopy ,这个是一个*native 方法,在ArrayList 对外提供的方法中,大量的使用了这个API,当频繁的对列表中的元素进行 add/remove 操作是,会比较费资源。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

附录

1. transient 是什么

transient 关键字在 Java 中的作用是指定某个类的成员变量不参与序列化过程。序列化是将对象的状态转换为字节流的过程,常用于将对象保存到文件中或通过网络传输。而使用 transient 关键字修饰的变量在对象被序列化时不会被保存,这样可以防止不必要或敏感的数据被序列化。

具体作用如下:

  1. 防止序列化敏感数据:某些类的字段可能包含敏感数据(如密码、个人信息),你不希望它们被序列化并保存到磁盘或通过网络传输。在这种情况下,可以使用 transient 关键字修饰该字段,序列化时就会跳过这个字段。
  2. 节省存储空间:对于某些不需要序列化的字段(例如缓存、临时变量等),使用 transient 可以避免不必要的数据占用存储空间。
  3. 防止不合法的数据:某些字段可能在对象被反序列化之后重新计算或初始化,如果这些字段被序列化了,可能会导致不合法的状态。在这种情况下,也可以使用 transient 来避免这些字段的序列化。

2. list中定义的最大长度为什么会减8

/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 的设置是为了处理 JVM 中数组分配的一些特定限制和内存管理问题。Integer.MAX_VALUE2^31 - 1,即 2147483647,是 Java 中 int 类型的最大值。但在实际中,JVM 为数组分配内存时,最大数组大小会比这个理论值略小。以下是为什么减去 8 的原因:

原因分析:

  1. JVM 的内存管理和数组头部
    • 在 JVM 中,数组对象除了存储数组的数据本身外,还需要存储一些额外的信息,比如数组的长度、类型信息等。这些信息占用了一部分内存,通常被称为数组的头部(header words)。
    • 具体需要多少字节来存储这些头部信息,依赖于 JVM 实现和对象内存布局。虽然不同 JVM 实现可能有所不同,但大多数 JVM 可能会保留几个字节(如 8 字节)用于这部分元数据。
  2. 避免 OutOfMemoryError
    • 通过减去 8,确保在数组分配时有足够的空间容纳这些头部信息,避免超出 JVM 对数组对象大小的限制。如果不进行这个调整,分配接近 Integer.MAX_VALUE 大小的数组可能会导致 OutOfMemoryError,因为 JVM 不能同时为数据和头部信息分配足够的内存。
    • 这个限制还可以避免一些边界情况下的问题,比如内存碎片或者其他 JVM 的内部管理机制的需求。
  3. 系统和实现的差异
    • 不同的 JVM 和平台可能有不同的数组大小限制。尽管理论上 Java 数组可以拥有 Integer.MAX_VALUE 个元素(即 2147483647 个),但在实际中,JVM 往往需要一些额外的空间来管理数组的结构。因此,MAX_ARRAY_SIZE 设置为 Integer.MAX_VALUE - 8 是一个安全边界,可以确保数组不会因为接近最大值而触发异常。

总结:

MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 是为了确保在 JVM 中分配接近最大容量的数组时,不会因为数组头部信息或 JVM 内存管理的限制而导致 OutOfMemoryError。减去的 8 字节主要是预留给数组的头部信息或其他内存管理开销。

3. System.arraycopy 方法

System.arraycopy(a, 0, elementData, size, numNew) 是 Java 中用于执行数组复制的代码,使用了 System.arraycopy 方法。这个方法能够快速、高效地复制数组中的元素。让我们详细解释这个调用的作用:

参数解析:


System.arraycopy(a, 0, elementData, size, numNew);
  1. a:源数组,表示要从中复制元素的数组。
  2. 0:源数组 a 中复制的起始位置,表示从 a 数组的第一个元素(索引 0)开始复制。
  3. elementData:目标数组,表示将元素复制到的数组。
  4. size:目标数组 elementData 中开始粘贴元素的位置,表示将从目标数组的 size 索引开始粘贴数据。
  5. numNew:要复制的元素数量,表示从源数组 a 中复制多少个元素到目标数组。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 重头开始嵌入式第四十三天(硬件 ARM架构 汇编语言)
  • 【数据结构】点分治 点分树
  • 基于二分查找的动态规划 leetcode 300.最长递增子序列(续)
  • 即插即用篇 | DenseNet卷土重来! YOLOv8 引入全新密集连接卷积网络 | ECCV 2024
  • 修改系统显示大小修改系统屏幕密度
  • Ansible集群服务部署案例
  • 前台项目启动/打包报错 Error: error:0308010C:digital envelope routines::unsupported
  • 【2】图像视频的加载和显示
  • 从准备面试八股文,感悟到技术的本质
  • 工具介绍---效率高+实用
  • 立体仓库WCS功能设计:物流自动化的智能核心
  • Superset二次开发之Git篇git fetch 异常信息汇总
  • Vue 3 中 Props 的使用指南
  • freeRDP OPenssl
  • Linux云计算 |【第四阶段】NOSQL-DAY3
  • -------------------- 第二讲-------- 第一节------在此给出链表的基本操作
  • 2019年如何成为全栈工程师?
  • axios 和 cookie 的那些事
  • eclipse(luna)创建web工程
  • Fundebug计费标准解释:事件数是如何定义的?
  • java8-模拟hadoop
  • JS 面试题总结
  • Just for fun——迅速写完快速排序
  • k8s 面向应用开发者的基础命令
  • Linux中的硬链接与软链接
  • Making An Indicator With Pure CSS
  • NSTimer学习笔记
  • PHP 7 修改了什么呢 -- 2
  • Redis在Web项目中的应用与实践
  • SpiderData 2019年2月13日 DApp数据排行榜
  • Spring框架之我见(三)——IOC、AOP
  • vue 配置sass、scss全局变量
  • 观察者模式实现非直接耦合
  • 全栈开发——Linux
  • 让你成为前端,后端或全栈开发程序员的进阶指南,一门学到老的技术
  • 设计模式走一遍---观察者模式
  • 用 vue 组件自定义 v-model, 实现一个 Tab 组件。
  • TPG领衔财团投资轻奢珠宝品牌APM Monaco
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • ‌分布式计算技术与复杂算法优化:‌现代数据处理的基石
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • (4)(4.6) Triducer
  • (6)设计一个TimeMap
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (echarts)echarts使用时重新加载数据之前的数据存留在图上的问题
  • (java)关于Thread的挂起和恢复
  • (python)数据结构---字典
  • (zhuan) 一些RL的文献(及笔记)
  • (附源码)spring boot北京冬奥会志愿者报名系统 毕业设计 150947
  • (附源码)ssm学生管理系统 毕业设计 141543
  • (离散数学)逻辑连接词
  • (七)Flink Watermark
  • (三)elasticsearch 源码之启动流程分析
  • (一) 初入MySQL 【认识和部署】