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扩容原理分析的更多相关文章

  1. ArrayList 扩容原理

    面试中经常问到的问题之一就是List的扩容机制了,他是怎么做到扩容的,大家都能答出来底层是数组,复制一个数组来扩容,但是再具体一点来说,大家就不知道该怎么说了,如果不看源码说这么多确实就差不多了,但是 ...

  2. ArrayList实现原理分析

    ArrayList使用的存储的数据结构 ArrayList的初始化 ArrayList是如何动态增长 ArrayList如何实现元素的移除 ArrayList小结 ArrayList是我们经常使用的一 ...

  3. C++ STL vector扩容原理分析

    扩容特点: 1)新增元素:vector通过一个连续的数组存放元素,如果集合已满,在新增数据的时候,就要分配一块更大的内存,将原来的数据复制过来,释放之前的内存,在插入新增的元素: 2)对vector的 ...

  4. Junit 注解 类加载器 .动态代理 jdbc 连接池 DButils 事务 Arraylist Linklist hashset 异常 哈希表的数据结构,存储过程 Map Object String Stringbufere File类 文件过滤器_原理分析 flush方法和close方法 序列号冲突问题

    Junit 注解 3).其它注意事项: 1).@Test运行的方法,不能有形参: 2).@Test运行的方法,不能有返回值: 3).@Test运行的方法,不能是静态方法: 4).在一个类中,可以同时定 ...

  5. 「必知必会」最细致的 ArrayList 原理分析

      从今天开始也正式开 JDK 原理分析的坑了,其实写源码分析的目的不再是像以前一样搞懂原理,更重要的是看看他们编码风格更进一步体会到他们的设计思想.看源码前先自己实现一个再比对也许会有不一样的收获! ...

  6. ArrayList源码深度剖析,从最基本的扩容原理,到魔幻的迭代器和fast-fail机制,你想要的这都有!!!

    ArrayList源码深度剖析 本篇文章主要跟大家分析一下ArrayList的源代码.阅读本文你首先得对ArrayList有一些基本的了解,至少使用过它.如果你对ArrayList的一些基本使用还不太 ...

  7. C#深入研究ArrayList动态数组自动扩容原理

    1 void Test1() { ArrayList arrayList = new ArrayList(); ; ; i < length; i++) { arrayList.Add(&quo ...

  8. ArrayList实现原理及源码分析之JDK8

    转载 ArrayList源码分析 一.ArrayList介绍 Java 集合框架主要包括两种类型的容器: 一种是集合(Collection),存储一个元素集合. 一种是图(Map),存储键/值对映射. ...

  9. 简单复习一下ArrayList的扩容原理

    刚刚跟几个好朋友喝完小酒回家,简单大概复习一下ArrayList的扩容原理,由于头有点小晕,就只大概说一下扩容的原理哈: 首先ArrayList实现了List接口,继承了AbstractList,大家 ...

随机推荐

  1. 031.SAP上查看所有的用户账号,查询SAP用户账号的后台数据库表

    01. 输入事务代码SU11, 然后输入SAP用户账号数据表USER_ADDR 02. 点击实用程序,再点击内容 03.点击查询 04. 将查看到的结果通过Excel表格导出 不忘初心,如果您认为这篇 ...

  2. class(二)--派生类的继承

    前言 从我之前的一篇笔记对象的继承中, 我们可以知道JS的继承方式依赖原型链,而比较好的继承方式是寄生组合式继承 先来温习下什么是寄生组合式继承 function Rectangle(length, ...

  3. .NET via C#笔记12——泛型

    12 泛型 使用值类型作为参数的泛型容器,传入值类型的参数时,不需要进行装箱 12.1 FCL中的泛型 System.Array中提供了很多泛型方法 AsReadOnly BinarySearch C ...

  4. 小米手机收到升级鸿蒙OS提示?官方回应

    虽然尚未得到官方确认,但华为“鸿蒙”OS已经成为网络热门话题,在机圈引发热议. 本周,互联网上出现了显示为MIUI 10手机被锁定,屏幕上出现“小米将于2020年9月15日全面停止服务,届时您所有设备 ...

  5. DragonFlyBSD 5.6 RC1 发布

    导读DragonFlyBSD操作系统下一个主要版本的第一个候选版本RC1现在可以进行测试.DragonFlyBSD 5.6将成为一个功能更新的版本,该功能更新将于周二晚上发布,是第一个候选版本,同时将 ...

  6. Java多线程之Java内存模型

    如果要了解Java内存模型,就得对多线程的三大特性有初步的了解. 1.原子性:独一无二.一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行.比如i = i+1:其中就包 ...

  7. Java对象序列化输入输出

    在网上看到一篇有关于对象序列化的代码,自己仿着写了把 在Java中,entity通过implements Serializable,然后使用ObjectInputStream和ObjectOutput ...

  8. HDU 4921 Map DFS+状态压缩+乘法计数

    算最多十条链,能截取某前缀段,每种方案都可以算出一个权值,每种方案的概率都是总数分之一,问最后能构成的所有可能方案数. 对计数原理不太敏感,知道是DFS先把链求出来,但是想怎么统计方案的时候想了好久, ...

  9. 干货分享:如何搞定Essay Paragraph部分?

    想要写出一篇高质量的留学生作业,首先要从写好段落(paragraph)开始.那么今天就随小编一起来看看,如何写好Paragraph部分? 段落:在英文中我们俗称为paragraph,而一篇英文文章通常 ...

  10. [题解] LuoguP6075 [JSOI2015]子集选取

    传送门 ps: 下面\(n\)和\(k\)好像和题目里的写反了...将就着看吧\(qwq\) 暴力打个表答案就出来了? 先写个结论,答案就是\(2^{nk}\). 为啥呢? 首先你需要知道,因为一个集 ...