ArrayList源码分析001 – 普通构造方法 + 普通增删改查:
首先我们使用空参构造方法创建一个集合:
List list = new ArrayList();
可以看到,源码中的操作,初始化一个空数组(Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};)并且复制给属性elementData
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* 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;
}
接着我们添加一条数据:
List list = new ArrayList();
list.add(1);
可以看到add()方法首先将属性size[ArrayList的大小(它包含的元素数)] + 1 这里其实就是记录数组添加这个元素之后的元素个数) 然后执行 ensureCapacityInternal(size + 1);方法
往下走可以看到ensureCapacityInternal(size + 1); 首先执行calculateCapacity(elementData, minCapacity)方法,将返回值作为ensureExplicitCapacity();的参数然后执行,
接着走先看一下calculateCapacity(elementData, minCapacity)方法里面做了什么操作,
首先它判断当前的elementData 是否是初始化数组( if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { )如果是则取当前的minCapacity(size+1)和DEFAULT_CAPACITY(默认初始化容量)的最大值返回,如果elementData 不是初始化数组直接返回minCapacity(size+1),
calculateCapacity(elementData, minCapacity)方法执行完毕,然后将它的返回值作为ensureExplicitCapacity()的参数,
看一下这个ensureExplicitCapacity()方法的执行过程:
首先它将modCount属性(modCount++记录一次操作)自增,紧接着判断minCapacity 是否大于elementData数组长度(minCapacity - elementData.length > 0)如果大于数组长度则执行grow(minCapacity);方法 [该方法是给数组一个扩容操作]
看一下 grow(minCapacity);方法 ,
这个方法首先获取到了当前数组的长度,之后将旧数组长度有符号右移一位(也就是扩充1.5倍)得到新的数组长度,之后接着判断新数组长度是否大于int最大值-8,如果大于则执行hugeCapacity(minCapacity);方法返回值赋值给新数组长度.
看一下hugeCapacity(minCapacity)方法:
首先判断minCapacity是否小于0如果小于0则直接抛出异常,接着三目运算,如果minCapacity>int最大值-8,则返回int类型最大值,如果小于则返回 int最大值-8,
接着grow(minCapacity);方法方法执行,Arrays.copyOf(elementData, newCapacity);拷贝一个新数组复制给elementData,
回到add()方法,
将值添加到数组中(elementData[size++] = e;)注意这里,这个时候size自增了一次,返回true
/**
* The size of the ArrayList (the number of elements it contains).
* ArrayList的大小(它包含的元素数)。
* @serial
*/
private int size;
/**
* Default initial capacity.
* 默认初始容量。
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* A constant holding the maximum value an {@code int} can
* have, 2<sup>31</sup>-1.
*/
@Native public static final int MAX_VALUE = 0x7fffffff;
/**
* The number of times this list has been <i>structurally modified</i>.
* Structural modifications are those that change the size of the
* list, or otherwise perturb it in such a fashion that iterations in
* progress may yield incorrect results.
*
* <p>This field is used by the iterator and list iterator implementation
* returned by the {@code iterator} and {@code listIterator} methods.
* If the value of this field changes unexpectedly, the iterator (or list
* iterator) will throw a {@code ConcurrentModificationException} in
* response to the {@code next}, {@code remove}, {@code previous},
* {@code set} or {@code add} operations. This provides
* <i>fail-fast</i> behavior, rather than non-deterministic behavior in
* the face of concurrent modification during iteration.
*
* <p><b>Use of this field by subclasses is optional.</b> If a subclass
* wishes to provide fail-fast iterators (and list iterators), then it
* merely has to increment this field in its {@code add(int, E)} and
* {@code remove(int)} methods (and any other methods that it overrides
* that result in structural modifications to the list). A single call to
* {@code add(int, E)} or {@code remove(int)} must add no more than
* one to this field, or the iterators (and list iterators) will throw
* bogus {@code ConcurrentModificationExceptions}. If an implementation
* does not wish to provide fail-fast iterators, this field may be
* ignored.
*/
/**
* 此列表在结构上被<i>修改的次数。
* 结构修改是指改变
* 列表,或以其他方式干扰它,使迭代
* 进展可能会产生错误的结果。
*
* <p>此字段由迭代器和列表迭代器实现使用
* 由{@code iterator}和{@code listIterator}方法返回。
* 如果此字段的值意外更改,则迭代器(或列表
* 迭代器)将在
* 对{@code next}、{@code remove}、{@code previous}的响应,
* {@code set}或{@code add}操作。这提供了
* <i>快速失败</i>行为,而不是
* 迭代过程中并发修改的情况。
*
* <p><b>子类使用此字段是可选的。</b>如果子类
* 希望提供快速失败迭代器(和列表迭代器),那么它
* 只需在{@code add(int,E)}中增加此字段,然后
* {@code remove(int)}方法(以及它重写的任何其他方法)
* 这导致了对列表的结构修改)。一次呼叫
* {@code add(int,E)}或{@code remove(int)}必须添加不超过
* 否则迭代器(和列表迭代器)将抛出
* 伪{@code ConcurrentModificationExceptions}。如果一个实现
* 不希望提供快速失败迭代器,此字段可能是
* 忽略。
*/
protected transient int modCount = 0;
/**
* 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;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* 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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
之后我们使用get(索引)方法来获取一条数据,
List list = new ArrayList();
System.out.println(i);
list.add(1);
System.out.println(list.get(0));
可以看到get(index)方法中首先调用rangeCheck(index);来检验索引是否越界如果它大于当前集合元素个数则抛出数组越界异常,
接着get(index)方法往下走,直接调用elementData(index);方法获取数组当前下标的元素并且返回;
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
/**
* Checks if the given index is in range. If not, throws an appropriate
* runtime exception. This method does *not* check if the index is
* negative: It is always used immediately prior to an array access,
* which throws an ArrayIndexOutOfBoundsException if index is negative.
*/
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// Positional Access Operations
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
我们在通过set方法来修改一下值
List list = new ArrayList();
System.out.println(i);
list.add(1);
System.out.println(list.get(0));
System.out.println(list.set(0, 2));
System.out.println(list.get(0));
可以看到首先调用rangeCheck(index);这个方法判断索引是否越界(详细看上面),接着调用elementData(index);方法获取旧数组的元素(详细看上),接着修改当前索引对应的值,并且将旧值返回
/**
* Replaces the element at the specified position in this list with
* the specified element.
*
* @param index index of the element to replace
* @param element element to be stored at the specified position
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
在看一下删除方法
List list = new ArrayList();
System.out.println(i);
list.add(1);
list.add(2);
list.add(2);
list.add(3);
System.out.println(list.get(0));
System.out.println(list.set(0, 2));
System.out.println(list.get(0));
System.out.println(list.remove(0));
调用remove(int index)删除对应索引的值,首先调用rangeCheck(index)检验当前索引是否越界,接着记录一次操作次数,通过elementData(index);获取当前索引对应旧值,之后执行
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
这里我个人理解是判断当前数组是否只有一个元素,同时记录之后要复制的数组元素的数量,如果不是则执行一个本地方法,这里我找了一下jdk1.8的api
System.arraycopy(elementData, index+1, elementData, index,numMoved);
public static void arraycopy(Object src,
int srcPos,
Object dest,
int destPos,
int length)
将指定源数组中的数组从指定位置复制到目标数组的指定位置。 阵列组件的一个子序列被从通过引用的源阵列复制src被引用的目标阵列dest 。 复制的组件数量等于length参数。 源阵列中位置srcPos至srcPos+length-1的组件分别复制到目标阵列的位置destPos至destPos+length-1 。
如果src个dest参数指代相同的数组对象,则被处理是否在位置上的部件进行复印srcPos通过srcPos+length-1首先被复制到一个临时的阵列length组分,然后将临时数组的内容被复制到的位置destPos通过destPos+length-1目标数组的。
如果dest是null ,那么会抛出一个NullPointerException 。
如果src是null ,则抛出一个NullPointerException并且目标数组不被修改。
否则,如果以下任一为真,一个ArrayStoreException抛出和不修改目标:
src参数是指不是数组的对象。
dest参数是指不是数组的对象。
src参数和dest参数是指组件类型是不同的基本类型的数组。
src参数是指具有原始组件类型的数组, dest参数是指具有引用组件类型的数组。
src参数是指具有引用组件类型的数组, dest参数是指具有原始组件类型的数组。
否则,如果以下任一为真,一个IndexOutOfBoundsException抛出和不修改目标:
srcPos论点是否定的。
destPos论点是否定的。
length论点是否定的。
srcPos+length大于src.length ,源数组的长度。
destPos+length大于dest.length ,目标数组的长度。
否则,如果来自位置srcPos至srcPos+length-1的源数组的任何实际组件无法通过转换转换为目标数组的组件类型,则抛出ArrayStoreException 。 在这种情况下,令k为比长度小于最小的非负整数,使得src[srcPos+ ķ ]不能转换到组件类型目标数组的; 当抛出异常时,位置srcPos至srcPos+ k -1源阵列组件将已经被复制到目标阵列位置destPos至destPos+ k -1 ,并且目标阵列的其他位置将不会被修改。 (由于限制已经列出,本段仅适用于两个数组都具有引用类型的组件类型的情况。)
参数
src - 源数组。
srcPos - 源数组中的起始位置。
dest - 目标数组。
destPos - 目的地数据中的起始位置。
length - 要复制的数组元素的数量。
异常
IndexOutOfBoundsException - 如果复制将导致数据外部数据界限的访问。
ArrayStoreException - 如果由于类型不匹配, src数组中的元素无法存储到 dest数组中。
NullPointerException - 如果 src或 dest是 null 。
测试一下这个方法
int test [] = {1,2,3,4};
System.arraycopy(test, 1+1, test, 1,
4 - 1 - 1);
for (int i : test) {
System.out.print(i+",");
}
运行结果:1,3,4,4,
接着回到remove(int index)方法,接着将数组最后一个值设为空elementData[–size]并 将size自减,然后返回旧值,虽然集合删除了一个元素但是注意elementData这个数组长度是没有变化的;
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices).
*
* @param index the index of the element to be removed
* @return the element that was removed from the list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}