ArrayList扩容机制解析
1.ArrayList的成员变量
首先我们先了解一下ArrayList的成员变量。
// 默认初始化大小
private static final int DEFAULT_CAPACITY = 10;// 空数组(用于空实例)
// 比如List<String> ls = new ArrayList<>(0);
private static final Object[] EMPTY_ELEMENTDATA = {};// 主要用来标识该ArrayList使用无参构造方法进行了初始化
// elementData等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA意味着使用无参构造函数进行了初始化
// 使用无参构造函数的默认容量是10,但是并不是一开始就进行了初始化,而是真正在插入元素的时候才进行了初始化
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};// 实际上保存ArrayList数据的数组
transient Object[] elementData; // non-private to simplify nested class access// ArrayList包含的元素个数
private int size;
2.ArrayList的构造方法
了解了基本成员变量以后我们再来看一下构造函数
// 有参构造方法,由自己进行容量的初始化
public ArrayList(int initialCapacity) {if (initialCapacity > 0) {// 初始化值大于0,创建initialCapacity大小的数组this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {// 初始化值等于0,则创建空数组this.elementData = EMPTY_ELEMENTDATA;} else {// 初始化值小于0,抛出异常throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}
}// DEFAULTCAPACITY_EMPTY_ELEMENTDATA为0.初始化为10
// 意味着ArrayList的初始其实是空数组,当添加第一个元素的时候数组容量才变成10
public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}// 构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。
public ArrayList(Collection<? extends E> c) {elementData = c.toArray();if ((size = elementData.length) != 0) {// 如果elementData不是Object类型数据(c.toArray可能返回的不是Object类型的数组所以加上下面的语句用于判断)if (elementData.getClass() != Object[].class)elementData = Arrays.copyOf(elementData, size, Object[].class);} else {this.elementData = EMPTY_ELEMENTDATA;}
}
3.ArrayList的扩容机制
下面正式开始讲解ArrayList的扩容机制。
什么时候开始扩容,毫无疑问,当然是添加元素,而elementData的容量不够的时候进行扩容,所以我们从add()方法起手。
public boolean add(E e) {// 首先是将当前size+1作为参数,然后调用ensureCapacityInternal判断是否需要进行扩容ensureCapacityInternal(size + 1); // Increments modCount!!// 最后将元素放入容量足够未扩容/容量不够扩容过的elementData数组elementData[size++] = e;return true;
}
接下来我们跟进ensureCapacityInternal方法,看看到底发生了什么…
private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}private static int calculateCapacity(Object[] elementData, int minCapacity) {// 1.计算最小扩容量// 在前面也说过,如果elementData等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA// 就意味着该ArrayList刚刚调用完无参构造方法,这个地方正是该ArrayList第一次添加元素,将容量初始化为10的地方if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}// 返回最小扩容量return minCapacity;
}private void ensureExplicitCapacity(int minCapacity) {modCount++;// 2.判断是否需要进行扩容// 如果最小需要的容量大于数组elementData的容量,就意味着要进行扩容操作// 注意!不要把size和elementData的容量弄混了,一个是ArrayList当前存放元素的个数,一个是当前容量的大小!if (minCapacity - elementData.length > 0)// 3.如果判断需要进行扩容,则调用grow方法进行扩容grow(minCapacity);
}
通过以上几个方法的执行,如果确定最小需求容量minCapacity大于该ArrayList的当前容量,就会调用grow()方法进行扩容,我们再继续看grow()方法的内部实现。
private void grow(int minCapacity) {// 先获取旧容量oldCapacity// overflow-conscious codeint oldCapacity = elementData.length;// 采用右移计算(效率更高),将新容量newCapacity赋值为旧容量oldCapacity的1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);// 检查新容量是否符合最小容量需求,如果新容量小于最小容量需求,就将新容量设计为最小容量需求if (newCapacity - minCapacity < 0)newCapacity = minCapacity;// 再检查新容量newCapacity是否超出了ArrayList类所定义的最大容量// 如果超出了那么就将新容量设置为Integer的最大值,否则设置为Arraylist定义的最大容量if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// 最后将elementData按照新容量newCapcity拷贝到一个新数组返回elementData = Arrays.copyOf(elementData, newCapacity);
}private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
}
读到这里,可能你会好奇关于为什么ArrayList的默认最大容量为Integer.MAX_VALUE - 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;
大概意思是说,一些虚拟机会预留数组头部的大小,一般数组头部有8个字节,所以 这里要减掉头部的8个字节,不然的话,整个数组大小就超过int的最大值了。就会跑出OutOfMemoryError错误。
相关文章:
ArrayList扩容机制解析
1.ArrayList的成员变量 首先我们先了解一下ArrayList的成员变量。 // 默认初始化大小 private static final int DEFAULT_CAPACITY 10;// 空数组(用于空实例) // 比如List<String> ls new ArrayList<>(0); private static final Object[…...
jsp-----web应用与开发
jsp基本语法 jsp页面的基本结构 定义变量 <%! %> 表达式:变量、常量、表达式 <% %>代码块、程序段【jsp程序代码即jsp脚本】 <% %>注释 隐藏注释 不会显示在客户的浏览器上,即jsp页面运行后页面上看不到注释内容。同时也不会出…...
洛谷 P1201 [USACO1.1]贪婪的送礼者Greedy Gift Givers
题目链接:P1201 [USACO1.1]贪婪的送礼者Greedy Gift Givers - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目描述 对于一群 n 个要互送礼物的朋友,GY 要确定每个人送出的钱比收到的多多少。在这一个问题中,每个人都准备了一些钱来送礼物…...
php设计模式-组合模式的运用
介绍 PHP的组合模式是一种设计模式,用于将对象组合成树形结构以表示“部分-整体”的层次结构。该模式允许客户端统一处理单个对象和组合对象,使得客户端在处理对象时不需要知道对象是否为单个对象还是组合对象。 在组合模式中,有两种类型的…...
一文教会你如何简单使用Fegin进行远程服务调用
文章目录1、fegin的基本介绍2、fegin的基本使用步骤3、项目中的实际运用4、测试前言在分布式微服务中,少不了会进行不同服务之间的相互调用,比如A服务要调用B服务中的接口,如何简单方便的实现呢?fegin可以来帮助。 1、fegin的基本…...
OpenAI——CLIPs(代码使用示例)
OpenAI——CLIPs(打通NLP与CV) Open AI在2021年1月份发布Contrastive Language-Image Pre-training(CLIP),基于对比文本-图像对对比学习的多模态模型,通过图像和它对应的文本描述对比学习,模型能够学习到文本-图像对的匹配关系。它开源、多模态、zero-s…...
什么样的人更适合创业?那类人创业更容易成功?
创业是一项充满风险和机遇的事业,成功的创业者需要具备一定的素质和能力。那么,什么样的人更适合创业?哪类人创业更容易成功呢?本文将为您介绍几个适合创业的人群和成功创业者的共同特点。 具有创新精神的人 创业需要不断创新&am…...
JavaApi操作ElasticSearch(强烈推荐)
ElasticSearch 高级 1 javaApi操作es环境搭建 在elasticsearch官网中提供了各种语言的客户端:https://www.elastic.co/guide/en/elasticsearch/client/index.html 而Java的客户端就有两个: 不过Java API这个客户端(Transport Client&#…...
NFT的前景,元宇宙的发展
互联网的普及和数字技术的广泛应用,成为消费升级的新动力,在不断创造出更好的数字化生活的同时,也改变了人们的消费习惯、消费内容、消费模式,甚至是消费理念,数字经济时代的文化消费呈现出新的特征。 2020年有关机构工…...
C#基础教程20 预处理器指令
文章目录 C#预处理指令教程简介预处理指令格式指令名 参数预处理指令类型条件编译指令if#if 条件表达式宏定义指令总结C#预处理指令教程 简介 预处理指令是在编译代码之前进行的一种处理,可以让程序员在编译前根据需要对代码进行一些修改、调整或者控制。C#语言中的预处理指令…...
【FPGA】Verilog:时序电路设计 | 二进制计数器 | 计数器 | 分频器 | 时序约束
前言:本章内容主要是演示Vivado下利用Verilog语言进行电路设计、仿真、综合和下载 示例:计数器与分频器 功能特性: 采用 Xilinx Artix-7 XC7A35T芯片 配置方式:USB-JTAG/SPI Flash 高达100MHz 的内部时钟速度 存储器&#…...
国外SEO策略指南:确保你的网站排名第一!
如果你想在谷歌等搜索引擎中获得更高的排名并吸引更多的流量和潜在客户,那么你需要了解一些国外SEO策略。 下面是一些可以帮助你提高谷歌排名的关键策略。 网站结构和内容优化 谷歌的搜索算法会考虑网站的结构和内容。 因此,你需要优化网站结构&…...
Tik Tok新手秘籍,做好五点可轻松起号
新手做TikTok需要有一个具体的规划布局,如果没有深思熟虑就上手开始的话,很有可能会导致功亏一篑,甚至是浪费时间。因此,想要做好 TikTok,就必须从最基本的运营细节开始,一步一步来,下面为大家分…...
【Linux】网络入门
🎇Linux: 博客主页:一起去看日落吗分享博主的在Linux中学习到的知识和遇到的问题博主的能力有限,出现错误希望大家不吝赐教分享给大家一句我很喜欢的话: 看似不起波澜的日复一日,一定会在某一天让你看见坚持…...
回溯法——力扣题型全解【更新中】
(本文源自网上教程的笔记) 回溯基础理论 回溯搜索法,它是一种搜索的方式。 回溯是递归的副产品,只要有递归就会有回溯。 所以以下讲解中,回溯函数也就是递归函数,指的都是一个函数。 回溯法的效率 虽然…...
【华为机试真题详解 Python实现】分奖金【2023 Q1 | 100分】
文章目录 前言题目描述输入描述输出描述示例 1题目解析参考代码前言 《华为机试真题详解》专栏含牛客网华为专栏、华为面经试题、华为OD机试真题。 如果您在准备华为的面试,期间有想了解的可以私信我,我会尽可能帮您解答,也可以给您一些建议! 本文解法非最优解(即非性能…...
netlink进行网卡重命名
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/socket.h> #include <linux/if.h> #include <linux/netlink.h>#define MAX_PAYLOAD 1024 // 最大负载长…...
2023年春【数据分析与挖掘】文献精读(一)-1:针对COVID-19,使用聚类方法有效提取生物特性关联进而识别预防COVID-19的药物
分享给大家——动漫《画江湖之不良人》第四季片尾,主人公 李星云所说的一段话: 悠悠众生,因果循环,大道至简,世间若尽是不如意事, 越是执着,便越是苦,不如安下心来,看该看的风景,做好该做之事。 初行娆疆,所悟如此, 就像曾经有一位紫衣姑娘,第一次来中原时,一样…...
【Go自学第三节】Go的范围(Range)用法
Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。 在讲Go语言的range之前,我们先回顾下Python中range的用法 for i …...
【备战面试】每日10道面试题打卡-Day6
本篇总结的是计算机网络知识相关的面试题,后续也会更新其他相关内容 文章目录1、HTTP 与 HTTPS 有哪些区别?2、HTTPS的加密过程是什么?3、GET与POST有什么区别?4、讲讲HTTP各个版本的区别?5、HTTP与FTP的区别ÿ…...
STM32博物馆环境监控系统设计与实现
基于STM32的博物馆展柜环境监控系统设计1. 项目概述1.1 系统背景文物保护工作中,展柜微环境稳定性直接影响文物保存状态。传统人工巡检方式存在响应滞后、数据不连续等问题。本项目设计了一套基于STM32的智能化环境监控系统,可实时监测温湿度、光照、烟雾…...
3步构建智能无人机防御系统:从威胁识别到实时追踪的实践指南
3步构建智能无人机防御系统:从威胁识别到实时追踪的实践指南 【免费下载链接】Anti-UAV 🔥🔥Official Repository for Anti-UAV🔥🔥 项目地址: https://gitcode.com/gh_mirrors/an/Anti-UAV 一、安全威胁&#…...
MCP3202 12位SPI ADC驱动开发与嵌入式工程实践
1. MCP3202 12位串行ADC嵌入式驱动深度解析与工程实践1.1 芯片特性与系统定位MCP3202 是 Microchip 推出的低功耗、逐次逼近型(SAR)12位模数转换器,专为嵌入式系统中高精度模拟信号采集场景设计。其核心电气特性如下:参数规格工程…...
Python 字典遍历全攻略:5 种常用方法 + 性能对比 + 实战优化技巧
在 Python 开发中,字典(dict) 是最常用的数据结构之一,以键值对形式存储数据,具备查询快、易操作的特点。而字典的遍历是日常开发中高频操作 —— 从简单的数据读取,到大规模数据处理、接口返回值解析&…...
flbook电子书下载神器!用这招把网页变PDF(Python+JS双解法)
从网页到PDF:PythonJS双引擎实现FlBook电子书高效归档方案 在数字阅读时代,电子书平台已成为获取知识的重要渠道,但许多优质内容往往缺乏便捷的下载选项。对于技术从业者和数字内容管理者而言,掌握将在线电子书转化为可离线保存的…...
AR.js实战指南:如何在Web浏览器中构建高效增强现实应用
AR.js实战指南:如何在Web浏览器中构建高效增强现实应用 【免费下载链接】AR.js Image tracking, Location Based AR, Marker tracking. All on the Web. 项目地址: https://gitcode.com/gh_mirrors/arj/AR.js 在移动设备普及的今天,增强现实&…...
LangChainJS审计日志:AI操作可追溯性的完整指南
LangChainJS审计日志:AI操作可追溯性的完整指南 【免费下载链接】langchainjs 项目地址: https://gitcode.com/GitHub_Trending/la/langchainjs 在当今AI应用开发中,确保AI操作的可追溯性和透明性至关重要。LangChainJS提供了强大的审计日志系统…...
QGIS3.28最新版行政区合并避坑指南:县转市数据融合的3个关键检查点
QGIS 3.28行政区合并实战:县转市数据融合的3个关键检查点 当我们需要将县级行政区数据合并为市级边界时,看似简单的"线转面融合"操作背后,往往隐藏着诸多数据陷阱。许多中级用户在QGIS中执行这类操作时,明明步骤正确却频…...
158.基于matlab的用于分析弧齿锥齿轮啮合轨迹的输出齿轮啮合轨迹及传递误差程序已调通
158.基于matlab的用于分析弧齿锥齿轮啮合轨迹的输出齿轮啮合轨迹及传递误差程序已调通,可直接运行1. 引言:TCA技术的重要性与挑战 弧齿锥齿轮作为机械传动系统的核心部件,其啮合质量直接影响整个传动装置的可靠性、效率和使用寿命。齿面接触分…...
第4章 编码规范-4.3 导入规范
导入语句包括import语句和from…import语句,该语句需要位于编码注释和文件注释之后,全局变量和常量之前。建议每一条导入语句只导入一个模块。示例代码如下:# 资源包\Code\chapter4\4.3\0406.py# 建议每一条导入语句只导入一个模块import rei…...
