原文地址: https://jygod.github.io/2018/04/15/ArrayList%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90/
初始化
List<Person> list = new ArrayList<Person>();
进行初始化的时候,我们来分析具体是一个怎么样的过程。
/**
* 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
就是ArrayList中所存的元素,底层是一个数组。执行初始化语句的时候,默认引用了一个static的数组,这个数组在编译的时候被放入常量池,且大小始终为0。
为什么会有这样的设计呢?假如你不停的创建新的ArrayList,理论上就会在堆内存里面不停地开辟新的空间,而且new了新的ArrayList之后如果暂时不使用的话,岂不是会占用很多空间么?所以为了解决这个问题,初始化ArrayList的时候都会首先将其指向常量池中的一个长度为0的数组,这样的话,无论你new多少个没用的ArrayList,他们的引用都会指向着一个常量池中的数组。
然后进行 list.add(new Person("jy", 18));
对list进行添加元素。
添加元素
我们来看一下add()方法的源码:
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
其中,size表示的是这个ArrayList的element个数。我们先来看看 ensureCapacityInternal(size + 1);
是在干什么。
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
此时elementData还没有任何元素,是个空的数组。所以在像ArrayList中添加第一个元素时,minCapacity的值为10。接着再来看看 ensureExplicitCapacity(minCapacity);
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
这里进行了一个判断,当最小需要的容量(minCapacity)大于当前数组长度时,就进行 grow()
操作,当然我们可以猜到,这个 grow()
方法就是扩容的操作。
扩容
我们来看一看grow()
的源码:
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
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);
}
这里,oldCapacity的值为原数组的长度。int newCapacity = oldCapacity + (oldCapacity >> 1);
相当于 newCapacity = oldCapacity + oldCapacity / 2,使用移位运算效率高一些~
此时我们的 minCapacity = 10,oldCapacity = 0,newCapacity = 0,所以有 newCapacity - minCapacity < 0
,因此, newCapacity = minCapacity=10。
最后执行 elementData = Arrays.copyOf(elementData, newCapacity);
将elemtData数组的长度增加到10。
这里大致对ArrayList的底层原理做了个简单的分析,ArrayList的源码挺简单,就不进行每一句的分析了~其他部分看看就能明白了。