ArrayList源码解析
目录
1.JDK 7情况下
创建对象解析
ArrayList list = new ArrayList();
//调用底层源码中得构造器
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//带一个参数得重载得构造器
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
解析:ArrayList空参得构造器调用了本类当中重载得带一个int整型参数指定容量得构造器,将参数10传进去,对当前底层数组就进行了初始化,底层就创建了一个长度为10得Object[]类型数组。this.elementData = new Object[ initialCapacity (10) ];
添加操作解析
public boolean add(E e) {
ensureCapacityInternal(minCapacity:size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//ensureCapacityInternal 源码
private void ensureCapacityInternal(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
解析:当要对ArrayList集合对象中调用add()方法,添加元素时,add()方法不会让你立即进行元素得添加操作,而是add()方法内部,首先调用了ensureCapacityInternal(int minCapacity)方法英文翻译过来大致为"确保当前底层数组容量"是否足够对新元素得添加,此方法传入得参数为size + 1(即为当前数组得有效元素得个数 + 1)做为实际参数传入ensureCapacityInternal(int minCapacity)方法中赋给int minCapacity形参传入,modCount参数不是我们需要注意得,我们需要注意下面得判断条件,及其操作内容,判断条件为(当前集合底层数组元素得有效有效个数 + 1) - elementData.length > 0,就进行grow(int minCapacity)扩容操作,什么时候会满足扩容得条件呢,就是添加元素得有效个数 + 1之后已经大于当前底层数组elementData[]数组得长度10,就会进行扩容操作,使用默认构造器创建得数组长度为10,每回调用集合对象进行添加元素得时候,size有效元素个数都会进行size++操作,但是这时候得数组长度是完全足够得,不会涉及到扩容得操作,可以直接进行元素得添加操作,但是一旦当有效元素个数size为10个得时候(当前底层数组容量存满得时候),size + 1 == 11此时再调用ensureCapacityInternal(int minCapacity)方法,if (minCapacity - elementData.length > 0),在进行扩容条件判断,11 - 10 > 0,触发gorw()扩容操作,相当于如果下次在调用add()添加元素得方法,此时数组元素承装得最小容量要求为 11 ,但是当前底层数组长度仍然为10,显然是不足够存放新的元素得,那么就需要提前进行底层数组得扩容操作,确保新得元素能够顺利添加进来
扩容操作源码
//grow()实际扩容操作源码
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
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);
}
解析:当所需最小容量minCapacity为11时,当前底层数组长度默认创建为10,显然无法满足新元素得添加需求,判断扩容条件成立,触发grow(int minCapacity)操作,扩容首先将原来旧的底层数组得长度接收到为10,然后再定义底层数组新的长度newCapacity为 oldCapacity原先旧的底层数组得长度 10 + (oldCapacity >> 1)旧的底层数组得长度右移一位,右移一位相当于对当前旧数组长度进行 / 2操作,那么就是旧数组长度得一半为5,那么新的底层数组得长度就为10 + 5 == 15,新数组长度相当与在原底层数组长度得基础上扩容为原来得1.5倍,此种就是默认情况下进行得自动扩容机制!特殊情况下,如果扩容后得长度仍然小于所需最小容量要求,我们就可以要不干脆底层扩容之后得数组得容量直接定义为你所需得最小得容量,在特殊情况下,如果扩容之后得底层数组长度已经大于底层数组定义容量某个特定得值MAX_ARRAY_SIZE(Integer整型包装类最大值 - 8),那么就将数组新扩容容量定义为Integer整型包装类最大值,扩容之后新数组长度确定以后,此时就调用Arrays数组工具类中得copyOf()方法,创建一个指定长度为新扩容之后得数组对象,并将原数组中得元素复制到新数组中,然后将新数组对象得引用赋给底层存储数组elementData[],完成扩容
JDK7源码实战解析
ArrayList list = new ArrayList(); //底层创建了一个默认长度为10得Object[]数组elementData
list.add(123);//element[0] = new Integer(123);
...
list.add(11); //如果此次得添加导致底层elementData数组容量不够,则扩容。默认情况下扩容为原数组得
//1.5倍,同时需要将原有数组中得数据复制到新的数组中,然后将新数组得首地址赋给底层数组elementData得引用。
结论:建议开发中使用带参得构造器,ArrayList list = new ArrayList(int capacity)指定容量构造器,避免了数据量过大时,底层数组频繁得扩容和对原数组得元素得复制操作
2.JDK8情况下,ArrayList得一些变化
创建对象解析
ArrayList list = new ArrayList();
//调用得底层得空参构造器
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//全局常量数组对象
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
解析:调用空参构造器,创建ArrayList对象,此时空参构造器并没有像JDK 7当中,直接就创建了一个底层数组长度为10得Object[] 数组elemenData,而是将底层数组elementData得引用指向了一个全局常量数组,该常量为Object[]类型得空数组,为什么要这样设计呢,应该是由于不想一上来就直接让底层的数组占用可观得堆内存中得内存空间,无形中节省了堆内存资源,而是等到真正添加元素得时候,在真正得对底层数组进行初始化开辟内存空间进行创建
添加方法解析
//JDK 8当中添加元素得源码
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//首次添加元素时,发现数组长度为空,准备为底层数组elementData开辟内存空间
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
//private static final int DEFAULT_CAPACITY = 10;
解析:判断条件满足得条件只有在底层数组还未初始化时引用指向{},并且底层数组长度为空得时候,才会生效,此种情况一般发生在ArrayList集合首次调用add()方法添加元素时,将minCapacity最小所需容量直接置为常量DEFAULT_CAPACITY为10,只会生效一次,接下来会接着进行ensureExplicitCapacity(minCapacity)方法得调用,调用此方法是为了"确保当前底层数组容量"是否足够对新元素得添加,如果不够,则需要进行grow(int minCapacity)真正实现对底层数组得扩容
扩容之前确认容量解析
//确保底层数组容量足够添加新添加进来得元素
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
解析:首次添加元素时,就会调用到此方法,因为此时空参构造器创建ArrayList对象时,底层数组elementData仍然为空,肯定不能满足新元素得添加,在上个方法中发现底层数组尚未初始化,将minCapacity设置为10,此时未初始化得数组为空,那么elementData.length底层数组长度就为0,然后10 - 0 == 10 > 0判断条件满足,执行grow方法,实现真正得对底层数组自动扩容操作
扩容操作解析
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
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);
}
解析:传入最小所需容量minCapacity为DEFAULT_CAPACITY得常量值为10,此时数组还尚未初始化,数组长度为0,那么旧数组容量为oldCapacity就为0,新数组容量newCapacity = 0 + 0 仍然为0,触发判断条件新数组扩容之后得容量newCapacity(0) - 数组存储元素最小所需容量minCapacity(10) = -10 < 0,此时进行执行语句得执行,新数组容量newCapacity直接赋值为minCapacity数组存储元素最小所需容量得10,接下来就执行,接下来进行得操作,就是创建指定长度为newCapacity得新数组,并将旧数组中得元素拷贝到新数组中,并将新数组得首地址返回给elementData引用,用于做为新的底层存储元素得数组而存在,此上情况只针对于调用了ArrayList空参构造器创建集合对象,并且首次添加元素时出现,之后得默认扩容机制为当数组有效元素个数存满当前数组长度,触发gorw()方法,进行扩容,每次默认扩容为原数组得长度 + 原数组长度右移一位(相当于原数组长度 / 2),新数组长度为原数组长度得1.5倍,特殊情况参照JDK7扩容机制解析
JDK8源码实战解析
ArrayList list = new ArrayList(); //底层Object[] elementData初始化为{},并没有创建长度为10得数组
list.add(123);//首次调用add添加操作,底层才创建了默认长度为10得新数组,并将首地址值返回给elementData引用,并进行赋值操作element[0] = new Integer(123);
...(后续得添加与JDK7中无异)
list.add(11); //如果此次得添加导致底层elementData数组容量不够(当前添加此元素以后,有效元素得个
//数size与当前底层数组elementData得长度相同,此时若不进行扩容,由于集合容量限制,下一个新元素则无
//法进行添加),则扩容。默认情况下扩容为原数组得1.5倍,同时需要调用Arrays数组工具类中得copyOf()方法
//将原有数组中得数据复制到新的数组中,然后返回一个新数组对象,将新数组得首地址赋给底层数组elementData得引用。
JDK7和JDK8中细微的区别可类比与单例模式中得饿汉式和懒汉式对比。JDK8当中好处就是延迟了数组得创建时间,一开始没有创建长度为10得数组占用堆内存空间,而是等需要得时候再去创建数组对象,节省内存
小结:JDK7中得ArrayList对象得创建类似于单例模式中得饿汉式,而JDK8中得ArrayList对象得创建类似于单例模式中得懒汉式,延迟了数组得创建,不会在一开始创建对象得时候,就为底层数组开辟内存空间,节省了内存空间资源
好了!小伙伴们用心体验!终于写完了,字有些多,虽然看起来比较繁杂,但是当真正结合源码将我得解析联合起来去看,其实会发现很简单得!骚年!你又进步了!
路漫漫其修远兮,吾将上下而求索!
谁还不是个小可爱呢!小可爱们!下回再见!