ArrayList扩容原理分析
1:代码解读和分析
1.1:构造方法分析
1:
public ArrayList(int initialCapacity) {
if (initialCapacity > ) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == ) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
指定初始化容量的构造方法,当initialCapacity大于0时,立即new Object[initialCapacity];在一开始的时候就有了指定大小的数组在ArrayList内部;当initialCapacity==0时,则将EMPTY_ELEMENTDATA给elementdata,其中EMPTY_ELEMENTDATA是一个空数组。
这种情况下,size会是几?
2:
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
第二个构造方法是无参构造方法。在这里直接将this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;其中DEFAULTCAPACITY_EMPTY_ELEMENTDATA和EMPTY_ELEMENTDATA一样如下
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] 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 = {};
都是用private static final Object[]定义的两个空数组。
3:
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != ) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
第三种构造方法是用通过放入实现Collection接口的容器来当作该构造方法的初始数据,通过toArray方法将内部的数据拿出来,给
elementData的方法来构建一个ArrayList出来。如果形参集合的数据长度为0,还是要将EMPTIYDATA数组赋给elementData的;
若源集合内的数组长度不为0后,在判断数组类型,如果不为Object基本类型的化,则重新通过Arrays.copyOf方法创建一个新的数组,数据,长度不变,类型设置成Object类型。
注意:这里的size也已经在构造方法中更新了。
1.2:add方法分析
默认在数组尾部增加一个对象。
public boolean add(E e) {
ensureCapacityInternal(size + ); // Increments modCount!!
elementData[size++] = e;
return true;
}
在add放法内部,使用ensureCapacityInternal(size + 1);方法,下面我们看一下该方法的作用。
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
方法名字为:保证内部容量,该方法内部又通过calculateCapacity方法来计算最小的容量,输入的minCapacity是size+1,即在增加数据之前来引入当前数据所用的最小的数据长度+1为计算标准进行判断。
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
上面是calculateCapacity方法的内部的功能,分析如下如果当前ArrayList是通过无参的构造方法创建,且当前是第一次add数据,则返回默认的的DEFAULT_CAPACITY和minCapacity的较大值。其中前者是10,则在第一次add数据,当size+1=1小于DEFAULT_CAPACIT时,都回返回10。显然当不是第一次add数据时,在这里的if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ,显然不会成立了,则返回minCapacity!
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > )
grow(minCapacity);
}
经过calculateCapacity方法返回的结果送到ensureExplicitCapacity(int minCapacity)方法中,当第一次add数据时,经过calculateCapacity返回的结果是10,即默认的值,则会执行grow(minCapacity)方法。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> );
if (newCapacity - minCapacity < )
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > )
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
在grow方法中,将原始数组长度保存在oldCapacity中,将newCapacity取值为oldCapacity的1.5倍。那如果newCapacity小于输入的minCapacity,那么就会将minCapacity的值给newCapacity,则根据newCapacity的大小和原始的elementData通过Arrays.copyOf方法创建一个新的数据即可!
总结:通过无参ArrayList构造函数创建的ArrayList的内部数组是指向DEFAULTCAPACITY_EMPTY_ELEMENTDATA,在第一次add数据时,会通过上面的步骤,创建一个大小为10的elementData的数据在内部!通过指定大小的
1.2.1:非首次add
经过上面分析,我们再来分析一下第二次add数据的情况。此时ensureCapacityInternal(size + 1); size = 1,则ensureCapacityInternal(2),则calculateCapacity(Object[] elementData, int minCapacity),中minCapacity为2,直接返回2。再进入
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
其中,minCapacity为2,显然小于elementData.length,所以不会扩容!
1.2.2:数组已满时add
经过上面分析,我们再来分析一下在内置的数据满的时候的add数据的情况,按照1.2.1中分析的那样,再
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
中如果minCapacity大于elements.length时会进行grow,按照原来数组长度的1.5倍进行,扩容。
2:总结
1:ArrayList在使用无参构造方法创建时,在创建后到add前,其内部是一个空数组,当add第一个数据后,内部数组变成长度为10,当内部数组已满时,就变成原来数组长度的1.5倍!
2:使用指定长度,会立即创建一个指定大小的数据在内部。
3:指定集合参数的构造方法是,会将根据源集合数据创建一个长度,数据与源集合相等的Object类型的内部数组。
3:对比LinkedList
LinkedList底层是用双向链表实现的,所以它对元素的增加、删除效率要比ArrayList好;它是一个双向链表,没有初始化大小,也没有扩容的机制,就是一直在前面或者后面新增就好。
ArrayList扩容原理分析的更多相关文章
- ArrayList 扩容原理
面试中经常问到的问题之一就是List的扩容机制了,他是怎么做到扩容的,大家都能答出来底层是数组,复制一个数组来扩容,但是再具体一点来说,大家就不知道该怎么说了,如果不看源码说这么多确实就差不多了,但是 ...
- ArrayList实现原理分析
ArrayList使用的存储的数据结构 ArrayList的初始化 ArrayList是如何动态增长 ArrayList如何实现元素的移除 ArrayList小结 ArrayList是我们经常使用的一 ...
- C++ STL vector扩容原理分析
扩容特点: 1)新增元素:vector通过一个连续的数组存放元素,如果集合已满,在新增数据的时候,就要分配一块更大的内存,将原来的数据复制过来,释放之前的内存,在插入新增的元素: 2)对vector的 ...
- Junit 注解 类加载器 .动态代理 jdbc 连接池 DButils 事务 Arraylist Linklist hashset 异常 哈希表的数据结构,存储过程 Map Object String Stringbufere File类 文件过滤器_原理分析 flush方法和close方法 序列号冲突问题
Junit 注解 3).其它注意事项: 1).@Test运行的方法,不能有形参: 2).@Test运行的方法,不能有返回值: 3).@Test运行的方法,不能是静态方法: 4).在一个类中,可以同时定 ...
- 「必知必会」最细致的 ArrayList 原理分析
从今天开始也正式开 JDK 原理分析的坑了,其实写源码分析的目的不再是像以前一样搞懂原理,更重要的是看看他们编码风格更进一步体会到他们的设计思想.看源码前先自己实现一个再比对也许会有不一样的收获! ...
- ArrayList源码深度剖析,从最基本的扩容原理,到魔幻的迭代器和fast-fail机制,你想要的这都有!!!
ArrayList源码深度剖析 本篇文章主要跟大家分析一下ArrayList的源代码.阅读本文你首先得对ArrayList有一些基本的了解,至少使用过它.如果你对ArrayList的一些基本使用还不太 ...
- C#深入研究ArrayList动态数组自动扩容原理
1 void Test1() { ArrayList arrayList = new ArrayList(); ; ; i < length; i++) { arrayList.Add(&quo ...
- ArrayList实现原理及源码分析之JDK8
转载 ArrayList源码分析 一.ArrayList介绍 Java 集合框架主要包括两种类型的容器: 一种是集合(Collection),存储一个元素集合. 一种是图(Map),存储键/值对映射. ...
- 简单复习一下ArrayList的扩容原理
刚刚跟几个好朋友喝完小酒回家,简单大概复习一下ArrayList的扩容原理,由于头有点小晕,就只大概说一下扩容的原理哈: 首先ArrayList实现了List接口,继承了AbstractList,大家 ...
随机推荐
- RDD 可视化 —— RDDOperationScope.withScope
最近在看各种博客,学习 spark 源代码. 网上对源代码的分析基本都是基于 0.7, 0.8, 1.0 的代码,而现在的发行版已经是 1.5 了.所以有些代码不大对的上.比如函数 RDD.map() ...
- gem5-gpu 选项解释
‘--split’ ,该项用于模拟独立显卡,GPU与CPU不在同一芯片上,两者的内存物理上隔绝,使用PCI-Express连接. ‘--access-host-pagetable’,该项用于模拟集成显 ...
- Django(六)实战2:向数据库添加,删除数据、重定向写法、重定向简写
一.向数据库添加图书数据 [上接]https://blog.csdn.net/u010132177/article/details/103831173 1)首先开启mysql服务,并运行项目 启动my ...
- python-处理json、处理时间
1.处理json:import json #json串就是字符串. d = { 'car1':{'color':'red','price':100,'count':50}, 'car2':{'colo ...
- ffmpeg “inttypes.h”: No such file or directory
编译过程:错误一:无法打开包括文件:“inttypes.h”: No such file or directory解决方法:删除之,并在其之前添加如下代码: #if defined(WIN32) &a ...
- Oracle-SQL 建表
建立员工分类表: 员工分类表结构.内容分别如下图: 一.使用PL/SQL Dev 这类可视化工具直接创建表 1.建立表结构 新建-table-名称(egrade) 然后 列:创建表结构 2. ...
- Ajax学习系列——创建XMLHttpRequest对象
Ajax - 创建XMLHttpRequest对象 首先介绍什么是XMLHttpRequest: XMLHttpRequest是Ajax的基础.中文可以解释为可扩展超文本传输请求.术语缩写为XHR. ...
- mvvm双向绑定机制的原理和代码实现
mvvm框架的双向绑定,即当对象改变时,自动改变相关的dom元素的值,反之,当dom元素改变时,能自动更新对象的值,当然dom元素一般是指可输出的input元素. 1. 首先实现单向绑定,在指定对象的 ...
- 【转】美团 MySQL 数据实时同步到 Hive 的架构与实践
文章转载自公众号 美团技术团队 , 作者 萌萌 背景 在数据仓库建模中,未经任何加工处理的原始业务层数据,我们称之为ODS(Operational Data Store)数据.在互联网企业中,常见的 ...
- 六、React 键盘事件 表单事件 事件对象以及React中的ref获取dom节点 、React实现类似Vue的双向数据绑定
接:https://www.cnblogs.com/chenxi188/p/11782349.html 事件对象 .键盘事件. 表单事件 .ref获取dom节点.React实现类似vue双向数据绑定 ...