一、ArrayList的结构
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
public interface RandomAccess {
}
其继承的AbstractList,同时又实现RandomAccess这个算法标记接口。
二、成员变量
1、DEFAULT_CAPACITY
private static final int DEFAULT_CAPACITY = 10;
这个是初始化的时候的默认容量。
2、EMPTY_ELEMENTDATA
private static final Object[] EMPTY_ELEMENTDATA = {};
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
这个是一个默认的空数组实例。
3、DEFAULTCAPACITY_EMPTY_ELEMENTDATA
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
这个也是用来在最开始初始化的,可以看到其在"EMPTY_ELEMENTDATA",加了"DEFAULT_CAPACITY",也就是说这个ArrayList用默认的容量去初始化,有这两种构造函数可以看到,如果传入了初始化的容量,传入为0则用EMPTY_ELEMENTDATA这个初始数组。
4、elementData
transient Object[] elementData; // non-private to simplify nested class access
这个Object数组就是用来放ArrayList添加的元素了,可以看到其的其的作用范围是default,与其处于同一包下的其他类也可以直接访问。
5、size
private int size;
该ArrayList中目前有多少元素。
6、MAX_ARRAY_SIZE
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
该ArrayList允许的最大容量。
三、构造函数
1、ArrayList(int initialCapacity)
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
这个构造函数使用传入的initialCapacity去创建一个对应容量是在再设置给elementData,如果为0,则使用EMPTY_ELEMENTDATA,如果<0则抛出异常不合法的参数 IllegalArgumentException。
2、ArrayList()
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
这个我们前面有简单提到提到,这种在我们第一次调用add方法的时候就会使用DEFAULTCAPACITY_EMPTY去初始化一个默认容量的对象数组。
3、ArrayList(Collection<? extends E> c)
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// defend against c.toArray (incorrectly) not returning Object[]
// (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
这个构造方法是将集合中的元素转换为一个数组再赋值给elementData,然后这里还多了一步,如果elementData.getClass()不是数组对象,则将其copy为对象数组再赋值给elementDate。如果集合入参为空,则将elementData初始化为EMPTY_ELEMENTDATA对象。
四、方法
1、add(E e)
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
这个方法是按顺序添加一个元素,可以看到其首先是modCount++,记录该ArrayList已经多修改了一次。然后调用add(e, elementData, size)方法去添加新元素。
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
这个是一个私有方法,如果目前放入的元素已经等于elementData数组的大小,则调用grow()方法去扩容,如果还没有满,则在数组对应s位置赋值,再将size大小+1;
2、add(int index, E element)
public void add(int index, E element) {
rangeCheckForAdd(index);
modCount++;
final int s;
Object[] elementData;
if ((s = size) == (elementData = this.elementData).length)
elementData = grow();
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
elementData[index] = element;
size = s + 1;
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
这个方法就是在指定的index位置添加(或替换)元素。首先是通过rangeCheckForAdd判断看其是否越界,在modCount自加,在判断如果size与elementData的长度相同,则需要对elementData进行扩容了,扩容后,再通过System.arraycopy将当前index位置的元素及其之后的元素往后移一位。然后在elementData的index位置将element添加,再size+1。
3、trimToSize
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
这个就是将elementData变为实际长度为size的设置,因为size表明的是目前在elementData实际放了多少个元素了,而elementData是扩容的时候固定的。这个方法就可以用来回收多余的空间。
4、ensureCapacity
public void ensureCapacity(int minCapacity) {
if (minCapacity > elementData.length
&& !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
&& minCapacity <= DEFAULT_CAPACITY)) {
modCount++;
grow(minCapacity);
}
}
这个就是根据给定的最小容量minCapacity的值去进行扩容,可以看到其是有条件的,需要大于目前elementData的长度,并且目前的elementData要不为DEFAULTCAPACITY_EMPTY_ELEMENTDATA&这个minCapacity要大于默认的DEFAULT_CAPACITY大小。
5、grow(int minCapacity)
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
这个就是根据minCapacity去进行扩容,其调用newCapacity方法。
6、grow()
private Object[] grow() {
return grow(size + 1);
}
这个去前面对应,minCapacity = size+1。
7、newCapacity
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
这个首先是获取原来elementData的长度,然后新的容量为原来容量的1.5( oldCapacity + (oldCapacity >> 1) ),然后判断newCapacity与传入的miniCapacity的大小,如果newCapacity更大,则使用newCapacity,不过下面还会再判断一次(可能出现大于MAX_ARRAY_SIZE的情况),如果传入的minCapacity更大,则先判断当前elementData是不是DEFAULTCAPACITY_EMPTY_ELEMENTDATA(创建对象的时候使用的是没有参数的构造方法,同时本次是第一次添加元素),则调用Math.max方法选取二者中更大的值。如果不是第一次了,则直接返回minCapacity。
8、hugeCapacity
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE)
? Integer.MAX_VALUE
: MAX_ARRAY_SIZE;
}
这个方法是判断minCapacity是不是已经大于MAX_ARRAY_SIZE了,如果大于则返回int类型的最大值,没有则返回MAX_ARRAY_SIZE。
9、elementData(int index)
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
返回index位置的元素,可以看到这个方法的作用于是default。
10、elementAt(Object[] es, int index)
static <E> E elementAt(Object[] es, int index) {
return (E) es[index];
}
获取es对象数组index位置的值,作用域为default。
11、get(int index)
public E get(int index) {
Objects.checkIndex(index, size);
return elementData(index);
}
获取index位置的元素,其首先进行范围判断,再调用elementData(index)方法获取对应元素返回。
12、set(int index, E element)
public E set(int index, E element) {
Objects.checkIndex(index, size);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
将元素设置到index位置同时返回该位置原来的元素。
13、remove(int index)
public E remove(int index) {
Objects.checkIndex(index, size);
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;
}
这个方法是删除指定index位置的元素,并将原来该位置的元素返回。这里的numMoved是用来判断这个index对于size(index为最后一个),这里之所以要再-1是因为size是从1开始,而index是从0开始的,如果不是最后一个,则通过System.arraycopy方法将elementData中的元素从index+开始往后的元素向前移动一位,再将最后一个元素置为null,size-1。
14、remove(Object o)
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
这个方法是删除指定元素(只删除一个),不会返回原来旧的元素。可以看到其for遍历调用equals方法判断,再调用fastRemove方法去设置index位置的元素。
15、fastRemove(int index)
private void fastRemove(int index) {
modCount++;
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
}
这个其实与前面的remove(int index)方法没有很大的区别,这个主要就是不会获取原来元素返回
16、clear()
public void clear() {
modCount++;
final Object[] es = elementData;
for (int to = size, i = size = 0; i < to; i++)
es[i] = null;
}
清理elementData中的元素。其只是简单的遍历置为null
17、removeRange(int fromIndex, int toIndex)
protected void removeRange(int fromIndex, int toIndex) {
if (fromIndex > toIndex) {
throw new IndexOutOfBoundsException(
outOfBoundsMsg(fromIndex, toIndex));
}
modCount++;
shiftTailOverGap(elementData, fromIndex, toIndex);
}
private void shiftTailOverGap(Object[] es, int lo, int hi) {
System.arraycopy(es, hi, es, lo, size - hi);
for (int to = size, i = (size -= hi - lo); i < to; i++)
es[i] = null;
}
删除fromIndex-toIndex之间的元素(不包括toIndex位置的值),可以看到其主要是将(size - hi)元素前移(hi后面的元素),在将最后面的这些元素置为null。
通过上面的这些元素我们也可以明显感觉到,使用ArrayList进行数据的插入、删除指定位置效率不高,因为其涉及到范围数据的整体copy移动。
18、removeAll(Collection<?> c)
public boolean removeAll(Collection<?> c) {
return batchRemove(c, false, 0, size);
}
设置c集合中元素。
19、retainAll(Collection<?> c)
public boolean retainAll(Collection<?> c) {
return batchRemove(c, true, 0, size);
}
保留c集合中的元素(不再c中的元素都删除)。
20、batchRemove(Collection<?> c, boolean complement,final int from, final int end)
boolean batchRemove(Collection<?> c, boolean complement,
final int from, final int end) {
Objects.requireNonNull(c);
final Object[] es = elementData;
final boolean modified;
int r;
// Optimize for initial run of survivors
for (r = from; r < end && c.contains(es[r]) == complement; r++)
;
if (modified = (r < end)) {
int w = r++;
try {
for (Object e; r < end; r++)
if (c.contains(e = es[r]) == complement)
es[w++] = e;
} catch (Throwable ex) {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
System.arraycopy(es, r, es, w, end - r);
w += end - r;
throw ex;
} finally {
modCount += end - w;
shiftTailOverGap(es, w, end);
}
}
return modified;
}
这个方法就是前面两个调用的方法,其中入参complement是决定用来保留还是用来删除的。
1)、如果传入的complement为false
就表示数据集合中参数,在第一个for循环的时候,就是找到第一个待删除的元素在elementData中位置(c.contains(es[r]) == false ,就一直循环)赋值给r。再将这个位置值赋值给一个新的局部变量w,然后r++将r调整到下一个位置,再从r开始遍历,遇到下一个不需要删除的元素(不在c中的元素)就赋值给w位置,再w++,以此循环。最后再通过shiftTailOverGap(es, w, end)方法,将最后位置w-end长度的元素删除。
2)、complement为false
这个就与上面的相反了,将在c中的元素往前移。
21、readObject(java.io.ObjectInputStream s)&writeObject(java.io.ObjectOutputStream s)
这两个就是从流中读取或者添加元素。
22、listIterator(int index)、listIterator()、iterator()
public ListIterator<E> listIterator(int index) {
rangeCheckForAdd(index);
return new ListItr(index);
}
public ListIterator<E> listIterator() {
return new ListItr(0);
}
public Iterator<E> iterator() {
return new Itr();
}
这两个方法就是获取遍历的迭代器实现。
五、ArrayList对于Iterator的实现
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
super();
cursor = index;
}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
ArrayList对于Iterator的实现有Itr、ListIterator,其大体逻辑与AbstractList中的实现差不多,这里就不再展开了(但其不是简单在原来写的那样去直接调用get、set方法等,而可能将get、set逻辑直接写在next这些方法中),我们直接来看下其在Itr中对next方法的实现。
1、next()
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
可以看到其整体逻辑与AbstractList的实现是类似的,这里是用的lastRet直接从elementData中获取了对应元素。
2、remove()
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
这个删除方法直接调用的ArrayList.this.remove(lastRet)方法。