内容简介:ArrayList是在实际项目中一个非常常用的类,今天我们通过源码来了解一下ArrayList的本质我们要使用ArrayList必定要先new一个出来,现在我们来看看new 到底做了写什么.在我们调用这样一个空参构造的时候,ArrayList实际上帮我们创建了一个空的数组,口说无凭,可以来看看
ArrayList是在实际项目中一个非常常用的类,今天我们通过源码来了解一下ArrayList的本质
内部变量说明
//默认容量
private static final int DEFAULT_CAPACITY = 10;
//使用有参构造创建ArrayList的时候,如果size=0那么elementData就会指向这个数组,或者调用trimToSize(0)方法也会指向这个空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//只有使用无参构造的时候才会将elementData指向这个空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//内部的元素数组,我们使用get()方法其实就是从其中取出元素
transient Object[] elementData; // non-private to simplify nested class access
//ArrayList的长度,默认为0
private int size;
//ArrayList的临界值,如果下一次扩容的数值大于这个数值,那么将elementData扩容到数组的最大值也就是0x7fffffff,如果最小需要的容量已经大于0x7fffffff那么则抛出OOM异常
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
复制代码
初始化
ArrayList()
我们要使用ArrayList必定要先new一个出来,现在我们来看看new 到底做了写什么.
ArrayList<Object> arrayList = new ArrayList<>(); 复制代码
在我们调用这样一个空参构造的时候,ArrayList实际上帮我们创建了一个空的数组,口说无凭,可以来看看
/**
* 构造一个空list并且初始化容量为10(如果只是单纯的new实际上并没有初始化容量)
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
复制代码
而这个 DEFAULTCAPACITY_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 = {};
复制代码
是的,他就是一个 长度为0的Object的对象数组 . 这个是一个 默认的静态被final修饰的的空数组实例 ,每一个array list实例被new出来一开始都共享这个空数组实例.
ArrayList(int initialCapacity)和ArrayList(Collection<? extends E> c)
当我们调用这两个方法中的其中一个方法的时候,如果 initialCapacity==0或者c.size()==0 那么内部的element将会指向 EMPTY_ELEMENTDATA
问题:不是已经有了一个空数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA)了为什么还要有一个空数组(EMPTY_ELEMENTDATA)呢?
用一个场景来解释这个问题
在我们创建ArrayList 的时候,如果使用ArrayList<>(int initialCapacity)指定初始化容量的方式来创建,这时候 传参是0那么,ArrayList内部的容器指向的就是这个EMPTY_ELEMENTDATA
在执行add方法的时候,如果是使用无参构造的方式创建的ArrayList 那么就会直接初始化其容量为10.
如果是使用有参构造的方式创建的ArrayList那么就会执行正常的扩容方式
说白了就是, DEFAULTCAPACITY_EMPTY_ELEMENTDATA就是一个标记,用来区分ArrayList是不是无参构造创建的ArrayList
添加元素
void add(E e)
当我们添加元素的时候只需要调用add方法就行了,但是你知道其背后到底执行了哪些动作吗?让我们接着往下看吧!
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
复制代码
我们来看看流程
看了流程之后,你可能会有诸多问题.
到底是怎么计算扩容的容量的?
添加元素是怎么添加的?
扩容容量是怎么计算的?
此处以**void add(E e)**为例
当我们添加一个元素的时候,如果这个元素是通过无参构造创建的(为了不让思路变得更复杂,此处只针对void add(E e)方法添加元素的容量计算),那么就将其容量初始化为 DEFAULT_CAPACITY ,也就是10(还记得开始我们介绍了其内部的变量吗,就是那个DEFAULT_CAPACITY)
看起来这样做就足够了,但是 java 为了避免溢出,又做了一层处理
新容量= 原来的容量+原来的容量/2这块有两层判断,
newCapacity-minCapacity<0 newCapacity-MAX_ARRAY_SIZE>0 (同newCapacity-Integer.MAX_VALUE - 8>0 )
第二个其实还想得通当超过最大数组长度的时候就将其 扩展到Integer.MAX_VALUE
那第一个新的容量为什么会小于最小的容量呢?
实际上是因为Integer超过最大范围再加会溢出直接变为-2^32.
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 以太坊源码分析(36)ethdb源码分析
- [源码分析] kubelet源码分析(一)之 NewKubeletCommand
- libmodbus源码分析(3)从机(服务端)功能源码分析
- [源码分析] nfs-client-provisioner源码分析
- [源码分析] kubelet源码分析(三)之 Pod的创建
- Spring事务源码分析专题(一)JdbcTemplate使用及源码分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
计算机网络(第6版)
[美] James F.Kurose、[美] Keith W.Ross / 陈鸣 / 机械工业出版社 / 2014-10 / 79.00元
《计算机网络:自顶向下方法(原书第6版)》第1版于12年前出版,首创采用自顶向下的方法讲解计算机网络的原理和协议,出版以来已被几百所大学和学院选用,是业界最经典的计算机网络教材之一。 《计算机网络:自顶向下方法(原书第6版)》第6版继续保持了以前版本的特色,为计算机网络教学提供了一种新颖和与时俱进的方法,同时也进行了相当多的修订和更新:第1章更多地关注时下,更新了接入网的论述;第2章用pyt......一起来看看 《计算机网络(第6版)》 这本书的介绍吧!