本文所用jdk版本是jdk1.7.0_79版本。
一.ArrayList类的定义
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
上面这一段代码是ArrayList类的定义,从定义上我们可以看出,ArrayList 继承了AbstractList抽象类,实现了List接口,Cloneable接口,Seriallizable接口。在这里我要说一点的就是,AbstractList继承了AbstractCollection抽象类,同时也实现了List接口,所以,我不太清楚如果ArrayList如果不实现List接口,会造成什么影响,这里我猜想应该不会造成什么影响,再一次实现List接口,可能是因为为了使意义更加清楚吧。关于AbstractList抽象类与List接口中又有哪些方法,感兴趣的话可以翻源码去看一下,这里就过多做介绍了。
二.ArrayList类的成员变量
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_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 == EMPTY_ELEMENTDATA will be expanded to
* DEFAULT_CAPACITY when the first element is added.
*/
private transient Object[] elementData;
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
上面4个是ArrayList的成员变量,从JDK注释我们可以事先了解一下他们的意义。
1.DEFAULT_CAPACITY是默认初始容量,这个默认初始容量的意思就是,ArrayList在创建对象时,默认的容量为10,如果容量不够,后面会有容量的扩展,关于扩展的方法,后面会有。
2.EMPTY_ELEMENTDATA是定义无参实例时,默认的参数集合。
3.elementData,用于存储ArrayList元素的缓冲数组。ArrayList的容量就是elementData数组的长度。任何空的同时elementData与EMPTY_ELEMENTDATA相等的ArrayList当第一个元素添加后,容量都将会扩展至默认容量。这里的emementData被transient修饰,该关键字的意思是该成员变量不用初始化。
4.size,设置ArrayList中包含的元素的数量。
三.ArrayList构造器
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
上述代码中可以看到,ArrayList共有三个构造器。
1.ArrayList第一个构造器中,传入一个int类型的参数,用于实例化ArrayList时,初始化elementData数组的长度。
2.ArrayList第二个构造器是一个无参构造器,仅执行一次elementData = EMPTY_ELEMENTDATA
3.ArrayList第三个构造器,传入了一个Collection类型的集合,用于实例化ArrayList时,将参数中的集合加入到实例化的ArrayList中。该构造器中,向几个成员变量进行了赋值,符合了几个成员变量存在的意义。
四.方法详解
4.1 trimToSize()方法
/**
* Trims the capacity of this <tt>ArrayList</tt> instance to be the
* list's current size. An application can use this operation to minimize
* the storage of an <tt>ArrayList</tt> instance.
*/
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = Arrays.copyOf(elementData, size);
}
}
该方法是用来将ArrayList的容量削减至与长度相等,从源代码中可以看到,所谓削减是将elementData的数组按照实际size的长度重新复制。看到这里的小伙伴相信绝大多数人应该可以理解了吧,其实ArrayList本质上就是用数组存储元素的,而且其值最终就是存储在elementData数组中的。所谓容量,就是指数组的最大长度,所谓size就是指数组中存储的元素的个数。
该方法中还有一个modCount++,这里,modCount变量是继承自AbstractList,我特意到源码中搜了一下,AbstractCollection抽象类与List接口中均没有该参数,可见是AbstractList抽象类中扩展的变量。具体作用可以看注释。
4.2 ensureCapacity(int minCapacity) 方法
/**
* Increases the capacity of this <tt>ArrayList</tt> instance, if
* necessary, to ensure that it can hold at least the number of elements
* specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != EMPTY_ELEMENTDATA)
// any size if real element table
? 0
// larger than default for empty table. It's already supposed to be
// at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
如有必要,增加此ArrayList实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数。
源代码中,首先定义一个minExpand,如果ArrayList实例不是空的,则赋值为0,如果是空的,则赋值为默认容量大小。再判断传入参数是否大于minExpand,如果大于,则执行ensureExplicitCapacity(minCapacity)方法。根据源码可知,该方法是一个私有方法,我们可以看一下它的源代码:
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
该源代码中,首先判断minCapacity是否大于elementData.length,如果大于,则执行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);
}
增加容量,以确保它可以容纳参数所指定的元素的个数。通过我们该方法,终于看到了该功能的底层是究竟如何处理的。
(1)首先定义一个oldCapacity存储原有的元素长度。(2)定义新的容量是旧容量的1.5倍。(3)判断外部传入的参数是否大于新的容量,如果大于,则外部传入的参数就为新的容量,如果不大于,则容量就扩展原容量的1.5倍。(4)判断新的容量是否超过MAX_ARRAY_SIZE(数组最大长度),如果超过,则通过hugeCapacity重新赋值。关于这个方法,做的什么事情,相信小伙伴很容易就能看懂。
总的来说,该方法的的作用是:在不考虑到达最大极限的时候。一般容量扩展最小是原容量的1.5倍,在1.5被仍不满足时,才会直接扩展至参数传入的容量值。
4.2 size()方法
public int size() {
return size;
}
该方法就是返回size的值,即ArrayList中的元素的个数。
4.3 isEmpty()方法
public boolean isEmpty() {
return size == 0;
}
该方法就是判断ArrayList是否为空。
4.4 contans(Object o)方法
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
该方法返回boolean类型,方法体中执行了一个判断indexOf(Object o) >= 0 的方法。下面看一下indexOf方法中的源码:
/**
* Returns the index of the first occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the lowest index <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
* or -1 if there is no such index.
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
该方法中,可以看出,是循环执行检索,将 o 同 elementData数组循环比较,如果有相等的,则返回该索引。如果没有,则返回-1。它的上一层即可判断出ArrayList是否包含 o 参数的值。
4.5 lastIndexOf(Object o)方法
/**
* Returns the index of the last occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the highest index <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
* or -1 if there is no such index.
*/
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
返回此ArrayList中最后出现 o元素的索引,如果没有,则返回-1。
源码中,可以看出来这里是以倒序的形式进行查询,与indexOf功能类似,只是查询顺序颠倒。
4.6 clone()方法
/**
* Returns a shallow copy of this <tt>ArrayList</tt> instance. (The
* elements themselves are not copied.)
*
* @return a clone of this <tt>ArrayList</tt> instance
*/
public Object clone() {
try {
@SuppressWarnings("unchecked")
ArrayList<E> v = (ArrayList<E>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
这里从单词意思和源码都可以看出,它其实是做了一次复制,将原ArrayList赋值一份,并且赋值出来的是一份将modCount归0的ArrayList。
4.7 toArray() 方法
/**
* Returns an array containing all of the elements in this list
* in proper sequence (from first to last element).
*
* <p>The returned array will be "safe" in that no references to it are
* maintained by this list. (In other words, this method must allocate
* a new array). The caller is thus free to modify the returned array.
*
* <p>This method acts as bridge between array-based and collection-based
* APIs.
*
* @return an array containing all of the elements in this list in
* proper sequence
*/
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
该方法是将ArrayList转化成数组。
4.8 toArray(T[] a)方法
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
该方法与上述类似,增加了一个泛型。
4.9 get(int 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);
}
代码中可以看出,该方法返回index位置的元素。在返回之前执行了一次判断,如果index >=size,则抛出IndexOutOfBoundsException,这里没有做index为负的判断,而是在获取数组值的时候,抛出ArrayIndexOutOfBoundsException,个人感觉这里也可以同时做index < 0的判断。
4.10 set(index, E element)方法
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
该方法是将ArrayList的index索引位的值,替换成element元素,并且返回老的元素。
4.11 add(E e)方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
该方法是向ArrayList中添加一个元素。
从源码中可以看出,在添加之前,首先执行了一次ensureCapacityInternal(size + 1) 方法,我们在前面介绍过类似的一个方法,而该方法的内部也调用了上面介绍过的那个方法,在这里就不再详细描述了,总之是在添加元素之前,判断是否需要扩展容量,如果有必要的话,就扩展一次容量。
该方法我们可以看出,在添加元素的时候,代码内部自动帮我们判断和扩展容量,不需要我们人工去做。
4.12 (1) remove(int index)方法
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;
}
该方法是用于删除索引位置的元素。
首先判断index值是否合法,否则抛出IndexOutOfBoundsException;然后执行删除,size减去1,最后返回删除的元素。
这里我们可以看到它删除的过程,实际上是将index+1索引位以及后面的所有元素,向前覆盖一位,再将最后一位置为null。这种方式比较低效,所以如果List需要不断地执行插入删除等操作,不建议使用ArrayList。
4.12(2) 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;
}
从代码中可以看出,该方法是执行删除o元素第一次出现的位置,如果删除成功,则返回true。删除方法为faseRemove(int index),并没有调用上面的remove(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相比,少了个校验,少了个返回删除的元素。因为调用此方法之前,已经执行了校验,不用重复校验,并且删除的元素就是参数o元素,所以也不用返回。所以fastRemove比remove少了两个环节,节省了一定的消耗。
4.13 clear() 方法
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
代码中可以看出,该方法执行一次循环,将elementData数组循环置为null,将size赋值为0。
4.14(1) addAll(Collection<? extends E> c)方法
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
该方法是将参数传入的集合一次性全部加入到此ArrayList中。
代码中可以看到,首先将参数中的集合c转为数组,必要的话扩展一次容量,再将参数转过的数组赋值到elementData中。
4.14(2)addAll(int index, Collection<? extends E> c)方法
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
该方法中比上一个方法,多了一个index参数,我们可以从代码上去理解。
首先执行一次index校验;再将集合c转化为数组a;下面是如果有必要,执行一次扩容;下面就是关键的执行插入的两点:(1),将index后面的元素向后挪,挪的位置就是c的长度;(2)再将转化的数组a,赋值到第一步腾出来的位置。
这里可以看出,该方法执行的插入,也是十分低效的,因为数组本身的原因,所以,这里也可以得出上面的结论,如果执行插入和删除是在List中间做的话,建议不要用ArrayList。
4.15 removeAll(Collection<?> c)
public boolean removeAll(Collection<?> c) {
return batchRemove(c, false);
}
该方法体中只执行了一次batchRemove(c,false)方法,可以看一下该方法的源码,做了哪些事情:
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
上面的方法逻辑有点复杂,我们来一点一点剖析。
(1)首先执行一次final Object[] elementData = this.elementData;这里就是定义一个局部变量,不在ArrayList本身上修改,如果出现异常则不会影响到原数据。
(2)执行一次for循环,这里的作用就是循环执行contains方法,将elementData中所有不能在c中找到的元素从0的位置开始重新赋值给elementData。这样能保证的是,所有不需要删除的元素,都被移到了最前面,w就是不需要删除的元素的个数。
(3)接下来将elementData中,w以后的元素置为null,size重新赋值成w。
分析到这里,相信大家已经知道整个方法的作用了,就是将ArrayList中包含了c集合中的元素删除掉,并且我们可以看出来,该方法也是非常低效的,因为删除元素的同时,对于保留的所有元素执行了大量的重新赋值。
由于时间问题,暂时先写一半,后续将在晚一些时间补上