ArrayList源码剖析
今天我们来探究一下ArrayList里面的究竟。。。本文采用Java8,下面是我自己结合源码对ArrayList的理解:
首先,在我们日常开发中经常也会用到ArrayList,为什么我们要使用ArrayList呢,而不使用其他的数据结构呢?
打开ArrayList源码:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
首先得明白ArrayList是继承Abstract这个类的,同样又实现了List集合接口,这个类中有这些属性:
一眼先看出ArrayList的默认容量是10,ArrayList的底层也是基于elementDate[]这个数组实现的,数组的大小是由size属性决定。
我们一般new一个ArrayList都是调用ArrayList无参数构造函数:
/**
* 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 = {};
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //初始化elementData 数组
}
虽然初始化了,但是要注意此时这个数组是空的,不确定数组的大小,也就是Java8初始化数组,并没有指定数组的大小,更要注意java7源码有所不同,如下:
//调用有参数构造方法对ArrayList进行初始化
public ArrayList(int initialCapacity){
super();
if(initialCapacity<0){
throw new IllegalArgumentException("Illegal Capacity:"+initialCapacity);
}
this.elementData=new Object[initialCapacity];
}
//无参数构造方法默认给定数组长度10
public ArrayList(){
this(10);
}
**所以这是Java7和Java8之间很重要的一点:
Java7调用无参数构造方法会初始化数组大小,Java8不会初始化数组大小
我们继续往下看:
ArrayList底层就是数组,在初始化的时候也是在底层new的一个数组,但是arrayList数组的长度并不是在一开始就给你创建好的 我们继续往下看数组的add方法:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private static final int DEFAULT_CAPACITY = 10;
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//真正的扩容,进行1,.5倍扩容
private void grow(int minCapacity) {
// 得到目前数组长度
int oldCapacity = elementData.length;
//给新的长度赋值:新容量等于老容量+老容量>>1(除以2)也就是1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
/对新数组进行拷贝 elementData老数组的数据 newCapacity新数组的数据
//然后通过copyof进行拷贝,拷贝到一个新的数组中(第一次进入的时候就是创建一个为长度为10的数组而已 不要搞错了)这也是为什么说数组的增删效率低,因为实际上数组的增删实际上都是类似于这个的拷贝操作
elementData = Arrays.copyOf(elementData, newCapacity);
}
总结:
- Java7使用ArrayLIst创建对象时,若未指出ArrayList数组大小,则调用默认构造进行初始化,大小为10,而Java8使用ArrayLIst创建对象时,若未指出ArrayList数组大小,则不设置数组大小,在第一次add的时候设置。
- 当调用add方法的时候,会与原集合数组大小比较,若不够,则进行1.5倍扩容,并拷贝数组传至新的扩容数组。