JDK8中ArrayList扩容机制

前言

这是基于JDK8的源码分析,在JDK6之前以及JDK11之后细节均有变动!!


首先来看ArrayList的构造方法

public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{private static final int DEFAULT_CAPACITY = 10;private static final Object[] EMPTY_ELEMENTDATA = {};private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};transient Object[] elementData; // non-private to simplify nested class accessprivate int size;//如果new ArrayList时指定了初始化容量大小public ArrayList(int initialCapacity) {if (initialCapacity > 0) {this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {//创建一个长度为0的空数组this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}}//默认创建一个空数组public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}public ArrayList(Collection<? extends E> c) {elementData = c.toArray();if ((size = elementData.length) != 0) {// c.toArray might (incorrectly) not return Object[] (see 6260652)// 官方bug,如果有子类继承了AttayList并且子类中也存在一个toArray方法时会出现getClass()不为Object的情况。文章最后会给案例进行解释if (elementData.getClass() != Object[].class)elementData = Arrays.copyOf(elementData, size, Object[].class);} else {// replace with empty array.this.elementData = EMPTY_ELEMENTDATA;}}
}

上述代码中该注释的地方已经进行了注释,以上代码可以总结如下信息:

  • 对于无参构造,ArrayList会创建一个默认的空数组
  • 对于提供了初始化大小的构造器,先判断初始化大小值是否合理,如果初始化大小指定为0的情况,将另一个空数组给elementData属性。
  • 对于参数为Collection的构造器,ArrayList会将传入的列表用迭代器按顺序返回。

对于无参或是指定初始化大小为0的情况,他们真正分配容量是在第一次执行add()方法。

现在以无参构造器创建的ArrayList为例(初始化大小为0与无参创建的ArrayList扩容逻辑略有不同),接下来去查看add方法的源码进一步分析扩容逻辑

public boolean add(E e) {// 填加元素之前,先调用ensureCapacityInternal方法,因为要判断新添加的元素能否被装进当前数组当中,因此要进行+1// 这里+1的说法是我的猜想,如有不正确或是更好的说法请指出ensureCapacityInternal(size + 1);  // Increments modCount!!// 执行添加元素的代码elementData[size++] = e;return true;
}

这里调用了ensureCapacityInternal方法,我们进一步查看该方法代码

// 根据给定的最小容量和当前数组元素来计算所需容量。
private static int ensureCapacityInternal(Object[] elementData, int minCapacity) {// 如果当前数组元素为空数组(初始情况),返回默认容量和最小容量(minCapacity此时为1)中的较大值作为所需容量if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//此时minCapacity为10}// 调用ensureExplicitCapacity方法ensureExplicitCapacity(minCapacity);
}// 确保内部容量达到指定的最小容量。
private void ensureExplicitCapacity(int minCapacity) {modCount++;// 以最小容量减去当前数组容量,小于则扩容if (minCapacity - elementData.length > 0)grow(minCapacity);
}

分析此时扩容逻辑:

  • 当第一次进入add方法时,进入ensureCapacityInternal方法中的if块,变量minCapacity被赋值为10。随后调用ensureExplicitCapacity方法,满足if条件,执行grow方法进行扩容
  • 第二次进入add方法时,进入ensureCapacityInternal方法中的if不会再满足,于是此时的minCapacity为2去调用ensureExplicitCapacity方法去调用,此时的elementData.length为10,不满足扩容条件,直接返回即可。
  • 当第十一次次执行add方法时满足ensureExplicitCapacity方法中的扩容条件,执行扩容。

接下来去查看grow方法源码,查看具体扩容逻辑

private void grow(int minCapacity) {// oldCapcatity为旧容量 newCapcatity为新容量int oldCapacity = elementData.length;// 每次扩容1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);// 当第一次扩容时,旧容量为10,新容量为0,因此交换新旧容量的值if (newCapacity - minCapacity < 0)newCapacity = minCapacity;// 当新容量大于数组最大容量时,则调用hugeCapacity方法if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win:elementData = Arrays.copyOf(elementData, newCapacity);
}private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();// 如果最小容量大于最大最大容量那么新容量就是Integer的最大值,否则返回数组最大容量(Integer.MAX_VALUE - 8)return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
}

具体的扩容逻辑注释的应该很明白了,这里就不细节分析了。

之前注释案例解释

针对第三个构造函数为什么存在toArray返回结果可能不为Object。我们来看以下案例

//继承ArrayList类,泛型随便指定,这里只是测试
public class Haha extends ArrayList<String> {//这里返回类型也是随便指定public String[] toArray(){return new String[]{"1","2"};}
}
public class Test {public static void main(String[] args){ArrayList<String> strings = new ArrayList<>();Object[] array = strings.toArray();System.out.println(array.getClass());strings = new Haha();array = strings.toArray();System.out.println(array.getClass());}
}

输出结果如下

因此在ArrayList源码中会存在将数组元素类型强转为Object部分。

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

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

相关文章

VUE3——Proxy API 与VUE2——defineProperty API区别

一、Object.defineProperty 定义&#xff1a;Object.defineProperty() 方法会直接在一个对象上定义一个新属性&#xff0c;或者修改一个对象的现有属性&#xff0c;并返回此对象 为什么能实现响应式 通过defineProperty 两个属性&#xff0c;get及set get 属性的 getter 函…

LeetCode刷题---查询近30天活跃用户数

1.给出满足的条件&#xff0c;截止至2019-07-27的近30天 activity_date BETWEEN DATE_ADD(2019-07-27,INTERVAL -29 day) and 2019-07-27这里使用了Between and 函数和 Date_add函数 2.按照日期分组&#xff0c;统计活跃用户个数 select activity_date day,count(distinct(us…

Python学习之-基础语法

第1关&#xff1a;行与缩进 任务描述 本关任务&#xff1a;改正代码中不正确的缩进&#xff0c;使其能够正常编译&#xff0c;并输出正确的结果。 编程要求 根据提示&#xff0c;改正右侧编辑器中代码的缩进错误&#xff0c;使其能够正确运行&#xff0c;并输出结果。 测试说明…

AcWing 1215. 小朋友排队 (树状数组)

n n n 个小朋友站成一排。 现在要把他们按身高从低到高的顺序排列&#xff0c;但是每次只能交换位置相邻的两个小朋友。 每个小朋友都有一个不高兴的程度。 开始的时候&#xff0c;所有小朋友的不高兴程度都是 0 0 0。 如果某个小朋友第一次被要求交换&#xff0c;则他的不…

蓝桥杯的数论总结

声明 我只是把数论相关的东西放在了一起&#xff0c;会有自己的理解&#xff0c;所以会有错误哈&#xff0c;欢迎在评论区中指出&#xff0c;另外有啥看不懂的也可以在评论区留言&#xff0c;我看到后就回你 质数 暴力 质数一般都是奇数&#xff0c;除了2小于2的不用考虑了…

Elasticsearch优化

集群配置 1、调整副本数&#xff1a;考虑数据的可用性和读取性能&#xff0c;合理配置分片的副本数。 2、合理配置分片大小(分片的合理容量&#xff1a;10GB-50GB)&#xff1a;避免分片过大&#xff0c;以确保更好的性能和均衡的负载。 3、监控集群状态&#xff1a;使用监控工…

mysql服务

任务描述:请安装mysql服务,建立数据表。 配置linux2为mysql服务器,创建数据库用户xiao,在任意机器上对所有数据库有完全权限。 创建数据库userdb;在库中创建表userinfo,表结构如下: 字段名 数据类型 主键

vue3+threejs新手从零开发卡牌游戏(十六):初始化对方手牌

添加对方手牌区时注意位置调整&#xff0c;以及手牌应该是背面朝上&#xff0c;加个rotateX翻转即可&#xff0c;其他代码和p1.vue代码一致&#xff0c;game/hand/p2.vue代码如下&#xff1a; <template><div></div> </template><script setup lan…

STL的基本概念

一、STL的诞生 长久以来&#xff0c;软件界一直希望建立一种可重复利用的东西 C的面向对象和泛型编程思想&#xff0c;目的就是复用性的提升 面向对象的三大特性(简单理解) 封装&#xff1a;把属性和行为抽象出来作为一个整体来实现事和物 继承&#xff1a;子类继承父类&a…

目前可用的免费云服务器整理汇总

随着云计算技术的飞速发展&#xff0c;越来越多的企业和个人开始关注并使用云服务器。为了吸引用户上云&#xff0c;各大云服务商纷纷推出了免费云服务器&#xff0c;供用户免费试用。本文将为大家整理汇总目前市场上可用的免费云服务器&#xff0c;以便大家更好地选择适合自己…

GIS与Python机器学习:开创地质灾害风险评价新纪元

地质灾害是指全球地壳自然地质演化过程中&#xff0c;由于地球内动力、外动力或者人为地质动力作用下导致的自然地质和人类的自然灾害突发事件。由于降水、地震等自然作用下&#xff0c;地质灾害在世界范围内频繁发生。我国除滑坡灾害外&#xff0c;还包括崩塌、泥石流、地面沉…

Verilog语言中parameter、localparam和符号常量的深度解析

1.parameter常量&#xff08;符号常量&#xff09; 用parameter来定义一个标示符&#xff0c;代表一个常量&#xff0c;称为符号常量 格式&#xff1a;parameter 参数名1 表达式&#xff0c; 参数名2 表达式&#xff1b;parameter 参数型数据的确认符&#xff0c;后边的为赋…

【C++】缺省参数

缺省参数是为函数的参数指定一个缺省值&#xff0c;调用该函数时&#xff0c;如果没有指定实参则采用该形参的缺省值。 void Test(int a 10) {cout << a << endl; }int main() {Test(); // 不传参默认10Test(100); // 指定传参100return 0; }缺省参数分为全缺省和…

【 代码随想录算法训练营第三十七天 | LeetCode738.单调递增的数字、968.监控二叉树】

代码随想录算法训练营第三十七天 | LeetCode738.单调递增的数字、968.监控二叉树 一、738.单调递增的数字 解题代码C&#xff1a; class Solution { private:// 判断一个数字的各位上是否是递增bool checkNum(int num) {int max 10;while (num) {int t num % 10;if (max &g…

Unity编辑器功能 将选中的文件夹复制一份到其他文件夹

[MenuItem("Ab包工具/将选中的文件移动到StreamingAssets文件夹下")] public static void MoveFireToStreamA() { //得到选中文件的数组 Object[] selectobj Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets); i…

1114. 棋盘问题

在一个给定形状的棋盘&#xff08;形状可能是不规则的&#xff09;上面摆放棋子&#xff0c;棋子没有区别。 要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列&#xff0c;请编程求解对于给定形状和大小的棋盘&#xff0c;摆放 k&#xfffd; 个棋子的所有可行的摆放…

web渗透测试漏洞流程:红队目标信息收集之基础资产信息收集

web渗透测试漏洞流程 1.企业目标信息的确认2.企业目标信息的收集1.2 企业目标信息的列表1.2.1 企业基本信息的收集1.2.1.1 企业信息收集框架1.2.1.2 企业员工信息收集1.2.1.3 企业主域名信息确认1.2.1.4 企业信息查询网站1.2.1.5 企业备案查询网站1.2.1.6 企业信用信息查询1.2.…

使用python读取csv文件快速插入postgres数据库

使用python读取csv文件快速插入postgres数据库 下面为完整代码 import pandas as pd import cStringIO import warnings from sqlalchemy import create_engine import sys reload(sys) sys.setdefaultencoding(utf8) warnings.filterwarnings(ignore) engine create…

Django之Web应用架构模式

一、Web应用架构模式 在开发Web应用中,有两种模式 1.1、前后端不分离 在前后端不分离的应用模式中,前端页面看到的效果都是由后端控制,由后端渲染页面或重定向,也就是后端需要控制前端的展示。前端与后端的耦合度很高 1.2、前后端分离 在前后端分离的应用模式中,后端仅返…

【物联网开源平台】tingsboard安装与编译

别看这篇了&#xff0c;这篇就当我的一个记录&#xff0c;我有空我再写过一篇&#xff0c;编译的时候出现了一个错误&#xff0c;然后我针对那一个错误执行了一个命令&#xff0c;出现了绿色的succes,我就以为整个tingsboard项目编译成功了&#xff0c;后面发现的时候&#xff…