ArrayList的扩容机制

news/2024/4/25 17:15:32/文章来源:https://blog.csdn.net/weixin_55697693/article/details/130380200

前置知识

ArrayList的底层实现是一个Object[],而LinkedList的底层实现是一个链表
ArrayList与LinkedList相对比:

  • ArrayList在随机访问时可以做到O(1),但是LinkedList的随机访问就是遍历链表,所以时间复杂度是O(N)
  • ArrayList在插入/删除元素时,需要移动额外的很多元素,但是LinkedList在插入/删除时无需移动其他元素,效率更高
  • 如果数据需要频繁的插入/删除,那么建议使用LinkedList
  • 如果数据较为稳定,希望高效的访问元素,建议使用ArrayList

利用反射获取ArrayList容量

由于ArrayList中并没有提供获取底层数组的方法,如何利用反射来获取ArrayList的真实容量

public static int getCapatity(ArrayList list) {  int len = 0;  try {  // 获取Class对象  Class<?> clazz = Class.forName("java.util.ArrayList");  // 获取类属性  Field field = clazz.getDeclaredField("elementData");  // 暴力打开权限  field.setAccessible(true);  Object[] objList = (Object[]) field.get(list);  // 返回数组长度  len = objList.length;  } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {  e.printStackTrace();  }  // 返回数组长度  return len;  
}

ArrayList的扩容机制

首先,创建ArrayList的方式有哪些
ArrayList提供了三个构造方法来创建ArrayList

// 1. 无参构造
public ArrayList() // 2. 指定容量大小
public ArrayList(int initialCapacity) // 3. 指定元素来创建  
public ArrayList(Collection<? extends E> c) 

无参构造

当调用无参构造来创建ArrayList时,看此无参构造方法的源码

public ArrayList() {  this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;  
}

其中elementData就是ArrayList实现的Object[ ],在无参构造中,直接为数据赋了一个默认的空元素值,也就是此时的容量为0
当ArrayList的容量不足时,扩容的机制是:
当目前的容量是0时,第一次扩容后的容量为10,以后每次扩容后的容量都是原始容量的1.5倍!!!
但是此处的1.5倍并不是在算术意义上的size*1.5,因为计算机并不能真正处理浮点数
此处是1.5倍是位运算的右移1位,能够避免浮点数的问题
即:

扩容后的容量 = 原始容量 + 原始容量 >> 1

当容量不足时,会创建一个新的指定大小的新数组,将原始数组中的内容拷贝到新的数组中(旧的数组不被再引用,就会被垃圾回收掉)
分析以下代码来试试:

// 1. 通过无参构造创建,此时容量为0
ArrayList list = new ArrayList();
// 2. 向容器中添加元素
list.add(5);
// 此时容量不足,触发第一次扩容,此时容量为10// 3. 继续添加10个元素,添加后共有11个元素
for(int i = 1; i <= 10 ;i ++){list.add(i);
}
// 当要插入第11个元素时,检查发现此时容量不足,触发第二次扩容
// 扩容后的容量为 = 10 + 10 >> 1 = 15;// 4. 此时有11个元素,容量为15
// 继续添加
for(int i = 1;i <= 10 ; i ++){list.add(i);
}
// 当添加第16个元素时,发现容量又不足了,所以此时触发第三次扩容
// 新的容量 = 15 + 15 >> 1 = 22

有参构造指定容量大小

来看利用ArrayList的指定容量大小的源码:如果指定的容量大于0,就会创建指定容量大小的数组。如果指定的容量小于0,抛出异常。

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);  }  
}

当指定了ArrayList的初始容量后,以后每次的扩容就是 扩容后的容量 = 原始容量 + 原始容量 >> 1,如果初始容量为0,还是会第一次扩容为10

指定元素创建

直接传入一个集合,来创建包含指定元素的ArrayList

ArrayList list = new ArrayList(Arrays.asList(1,2,3));

此时会使用指定的元素大小作为起始容量,以后的扩容规则还是同上。

addAll()

add()方法是一次添加一个元素,当使用addAll()方法可以一次添加多个元素。
以上的扩容机制都是在add()方法触发的情况下,新的容量 = 原始容量 + 原始容量 >> 1
当addAll()触发扩容时,扩容机制是不一样的。
会在 默认下次扩容后的容量大小当前所有的元素数量 之间选择一个较大值

ArrayList list = new ArrayList();
// 此时容量为0
list.add(Arrays.asList(1,2,3));
// 此时容量不足,触发扩容,
// 此时的元素数量是3,默认下次扩容后的容量是10
// 选择10 作为扩容后的容量

再来看一个例子

Array list = new ArrayList();
// 刚创建,此时容量为0
// 继续添加,容量不足,触发扩容
list.addAll(Arrays.asList(1,2,3,4,5,6,7,8,9,10,11));
// 默认下次扩容后的容量是10, 但是此时的元素数量是11,
// 所以扩容后的容量为11

再来看一个例子

ArrayList list = new ArrayList();
for(int i = 1;i <= 10 ;i ++){list.add(i);
}
// 此时容量为10,容量不足
list.addAll(Arrays.asList(1,2,3))
// 下次扩容后的容量为15,元素数量是13,
//  所以扩容后的容量是15

总结

三种创建ArrayList的方式:

  • ArrayList() 会创建一个起始容量为0的大小
  • ArrayList(int capacity)以指定的大小作为起始容量
  • ArrayList(Collection c)以指定的元素的数量作为起始容量

两种扩容机制:

  • add()如果原始容量是0,则扩容后的容量为10,以后每次扩容都是之前的1.5倍
  • addAll()会在下次扩容的大小和当前元素数量之间选择一个较大值

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_103776.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

每日学术速递4.25

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Long-Term Photometric Consistent Novel View Synthesis with Diffusion Models 标题&#xff1a;具有扩散模型的长期光度一致的新视图合成 作者&#xff1a;Jason J. Yu, Feresh…

Python 数据存储 ---->方式

我的个人博客主页&#xff1a;如果’真能转义1️⃣说1️⃣的博客主页 关于Python基本语法学习---->可以参考我的这篇博客&#xff1a;《我在VScode学Python》 数据存储是指在数据加工处理过程中将产生的临时文件或加工结果以某种格式保存。 常用的数据存储格式包括 TXT、Exc…

Ansys Zemax | 设计抬头显示器时要使用哪些工具 – 第一部分

本文演示了如何使用OpticStudio工具设计分析抬头显示器(HUD)性能&#xff0c;即全视场像差(FFA)和NSC矢高图。(联系我们获取文章附件) 初始结构 HUD简介 以下为HUD的示意图。液晶显示器作为光源发光&#xff0c;光线被HUD的两个反射镜反射&#xff0c;然后通过风挡玻璃反射&am…

【MySQL】MES中,发货计划取数逻辑

系列文章 C#底层库–MySQLBuilder脚本构建类&#xff08;select、insert、update、in、带条件的SQL自动生成&#xff09; 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/129179216 C#底层库–MySQL数据库操作辅助类&#xff08;推荐阅读&#xff0…

聊聊 IP packet 的 TTL 与 tcp segment 的 MSL

聊聊 IP packet 的 TTL 与 tcp segment 的 MSL 1 前言 - 网络知识的重要性 近几年在排查解决应用系统在客户现场遇到的复杂问题时&#xff0c;越来越觉得除了扎实的LINUX操作系统知识&#xff0c;对TCP/IP网络知识的深入理解也是至关重要的。 有鉴于此&#xff0c;后续笔者会…

启英泰伦智能语音芯片在语音控制吸顶灯上的应用解决方案

随着智能控制技术的不断发展&#xff0c;人们对于家用电器的功能需求越来越多&#xff0c;智能吸顶灯是一种常见的照明设备&#xff0c;通常被安装在室内房顶上面&#xff0c;除了具有传统吸顶灯的照明功能外&#xff0c;还添加了智能控制和自动化功能&#xff0c;如远程控制、…

必须要知道的hive调优知识(下)

Hive如果不用参数调优&#xff0c;在map和reduce端应该做什么 1、map阶段优化 Map阶段的优化&#xff0c;主要是确定合适的map数。那么首先要了解map数的计算公式 num_reduce_tasks min[${hive.exec.reducers.max}, (${input.size}/${hive.exec.reducers.bytes.per.reducer…

《一次性分割一切》阅读笔记

目录 0 体验 1 摘要 2 十个问题 参考文献 0 体验 体验地址&#xff1a;SEEM - a Hugging Face Space by xdecoder 体验结果&#xff1a; 将哈士奇和汽车人从图片中分割出来。 1 摘要 尽管对于交互式人工智能系统的需求不断增长&#xff0c;但在视觉理解&#xff08;例如…

Qt5.9学习笔记-事件(一)

⭐️我叫忆_恒心&#xff0c;一名喜欢书写博客的在读研究生&#x1f468;‍&#x1f393;。 如果觉得本文能帮到您&#xff0c;麻烦点个赞&#x1f44d;呗&#xff01; 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧&#xff0c;喜欢的小伙伴给个三…

对git的简单总结

Git的基本使用 配置用户名和邮箱常见的操作查看仓库的状态远端仓库整体流程分支本地分支命令远端分支命令 这几天在做毕业设计&#xff0c;需要用到git&#xff0c;所以简单总结一下git的基本使用。 配置用户名和邮箱 git config --global user.name "Your Name" g…

ai模型训练生成效果 chilloutmix_NiPrunedFp32Fix.safetensors

模型名称&#xff1a; chilloutmix_NiPrunedFp32Fix.safetensors 关键词 extremely detailed CG unity 8k wallpaper,(masterpiece),(best quality),(ultra detailed),(ultra realistic),(Best character details:1.2),dynamic angle,professional lighting, photon mapping, …

【22-23 春学期】人工智能基础--AI作业6-误差反向传播

老师发布作业链接&#xff1a;(429条消息) 【22-23 春学期】AI作业6-误差反向传播_HBU_David的博客-CSDN博客 目录 老师发布作业链接&#xff1a;(429条消息) 【22-23 春学期】AI作业6-误差反向传播_HBU_David的博客-CSDN博客 1.梯度下降 2.反向传播 3.计算图 4.使用Numpy…

【代理设计模式详解】C/Java/JS/Go/Python/TS不同语言实现

简介 代理模式&#xff08;Proxy Pattern&#xff09;是一种结构型设计模式&#xff0c;用一个类来代理另一个类或几个类的功能。 在代理模式中&#xff0c;我们创建具有现有对象的对象&#xff0c;以便向外界提供功能接口。 延迟初始化&#xff08;虚拟代理&#xff09;。如…

FPGA基础知识 LCMXO3LF-6900C-6BG400I FPGA可编程逻辑简介

FPGA是英文Field&#xff0d;Programmable Gate Array的缩写&#xff0c;即现场可编程门阵列&#xff0c;它是在PAL、GAL、CPLD等可编程器件的基础上进一步发展的产物。它是作为专用集成电路&#xff08;ASIC&#xff09;领域中的一种半定制电路而出现的&#xff0c;既解决了定…

喜报 | ScanA内容安全云监测获评“新一代信息技术创新产品”

4月20日&#xff0c;在赛迪主办的2023 IT市场年会上&#xff0c;“年度IT市场权威榜单”正式发布。 知道创宇的ScanA内容安全云监测产品荣获“新一代信息技术创新产品”奖项。作为中国IT业界延续时间最长的年度盛会之一&#xff0c;历届IT市场年会公布的IT市场权威榜单已成为市…

状态模式——随遇而安

● 状态模式介绍 状态模式中的行为是由状态来决定的&#xff0c;不用的状态下有不同的行为。状态模式和策略模式结构几乎完全一样&#xff0c;但它们的目的、本质却完全不一样就。状态模式的行为是平行的、不可替代的&#xff0c;策略模式的行为是彼此孤立、可相互替换的。用一…

微分方程数值解法(Runge-Kutta法PLC实现)

微分方程数值解法之欧拉法请参看下面的博客文章: 微分方程数值解法(PID仿真用一阶被控对象库PLC算法实现)_数学微积分算法plc编程实例_RXXW_Dor的博客-CSDN博客微分方程除极特殊情况外,大部分不可能求出它的精确解,只能用各种近似方法得到满足一定精度的近似解,微分方程由…

web端导航菜单系列

导航菜单属于导航中最常规的一种导航模式&#xff0c;它有2个显而易见的用途&#xff1a;帮助我们找到想要的任何东西和告诉我们现在身在何处。帮助用户在不同页面之间跳转找到目标功能。 导航作为网站或者平台的骨架&#xff0c;是产品设计中不容忽视的一环。结合自身对于导航…

如何建立Linux与git的连接?

文章目录 建立连接三板斧&#xff1a; 本文以Xshell为案例进行与git的连接&#xff01; 建立连接三板斧&#xff1a; add , commit ,push Linux与git远程连接的方法&#xff1a; 1.设置全局的用户名和邮箱 git config – global user.name “你的用户名” git config – glo…

Springboot Mybatis使用pageHelper实现分页查询

以下介绍实战中数据库框架使用的是mybatis&#xff0c;对整合mybatis此处不做介绍。 使用pageHelper实现分页查询其实非常简单&#xff0c;共两步&#xff1a; 一、导入依赖&#xff1b; 二、添加配置&#xff1b; 那么开始&#xff0c; 第一步&#xff1a; pom.xml添加依…