ArrayList就像是宰相的肚子,好像可以容纳无限多的元素,而且啥都能装。事实上,ArrayList也被称为动态数组,意思是可以动态扩容的数组,它实现了List接口的所有方法,并且允许向其中添加null元素。该实现与Vector基本一致,只是ArrayList是非同步的。
声明
首先看ArrayList的声明:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//内部代码
}
ArrayList继承了AbstractList,该类同样实现了List接口,同时实现了一些所有的list大都逻辑相同的操作方法,比如indexOf等。
RandomAccess,Cloneable,java.io.Serializable都是标记接口,分别表示该类是支持随机访问的、可复制的、可序列化的。
ArrayList中存放的元素放在了哪里呢?
变量
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
/**
* ArrayList的初始容量为10。如果不指定那么就是这个容量。
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 这种情况是用户指定了初始容量为默认的0个元素的情况。
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 用于指定当用户没有指定初始容量时的默认数组。就是用户没有传入初始容量。
*这种情况与上面需要区分开来,因为两种的扩容是的容量是不一样的。
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* ArrayList存放元素的地方,也就是说ArrayList中的元素就是存放在内部的elementData数组中。
* 如果不指定初始的容量,那么默认就是elementDate=DEFAULTCAPACITY_EMPTY_ELEMENTDATA,当第一个元素被
*添加的时候,elementData将被扩容为DEFAULT_CAPACITY
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
*数组的容量,这里并不是底层数组的大小,而是数组中到底存了多少个元素。
*size是小于等于capcity的。
*/
private int size;
/**
* 底层数组可以分配的最大空间,从这里可以看出,ArrayList也并不是无限扩容的。
* 如果尝试分配更大的空间给底层数组,将抛出OutOfMemoryError
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
}
注意: 还有一个相当重要变量成为modCount,该变量继承自AbstractList。这个变量是protected transient int 类型的。用来防止多个线程同时修改list的元素,并在出现结构性修改操作时执行fail-fast机制。但是这个机制并不能提供确切的保障。
初始化方法
ArrayList的初始化方式有三种:
-
不指定任何参数
- 指定初始容量
- 通过传入Collection实现类的实例构造
/**
* 只要我们在创建ArrayList时,传入了一个大于0的int类型的数,那么将会根据这个initialCapacity初始化elementData
* 如果我们传入的是0,那么就使用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);
}
}
/**
* 默认不传参数的时候,这个时候创建的时候是没有进行底层数组的初始化的。
*/
public ArrayList()
{
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 使用传入的Collection实现类的实例初始化底层数组,c中有多少个元素,此时elementData就有多大,也就是说
* 此时elementData是满的状态。如果此时c中没有元素,那么仍然使用EMPTY_ELEMENTDATA。
* c.toArray()返回的是一个全新的数组,并不会影响原先c中内部的数据。
*/
public ArrayList(Collection<? extends E> c)
{
elementData = c.toArray();
if ((size = elementData.length) != 0)
{
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else
{
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
通过三种构造函数可以看出,在不指定任何参数的时候,ArrayList采用的延迟初始化的方式,并没有真正的初始化一个容量为DEFAULT_CAPACITY的elementData数组。但是另外两种初始化方式,构造函数一旦执行,底层数组就不为空了(Collection中包含元素的话)。
常用方法
添加元素系
扩容逻辑:
private void ensureCapacityInternal(int minCapacity)
{
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/**
* 如果初始化时没有指定任何参数,并且底层的数组还没有初始化过,那么就确定要初始化的容量
* 为Math.max(DEFAULT_CAPACITY, minCapacity);
* 这里DEFAULT_CAPACITY=10,如果初始向ArrayList一次添加了超过十个元素,那么就有可能使用minCapacity初始化。
*/
private static int calculateCapacity(Object[] elementData, int minCapacity)
{
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
{
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
/**
* 因为要进行扩容,那么需要递增modCount,然后判断minminCapacity是否大于数组当前的容量,
* 如果大于了,那么就需要扩容了。
*/
private void ensureExplicitCapacity(int minCapacity)
{
modCount++;
// 这个代码可以防止minCapacity溢出
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* 要进行扩容以确保数组的容量可以满足minCapacity的要求。
* 扩容的机制是原数组容量*1.5,然后判断该容量是否可以满足minCapacity的要求。
* 如果还不满足,那么扩容后的新容量将直接为minCapacity。然后检查newCapacity是否溢出,
* 溢出抛出异常,如果minCapacity介于MAX_ARRAY_SIZE(Integer.MAX_VALUE-8)-Integer.MAX_VALUE之间
* 那么newCapacity=Integer.MAX_VALUE,
* 然后创建容量为newCapacity的新数组并将旧数组的元素复制到新数组中,然后将elementData指向新数组。
* 这样就完成了扩容工作
*/
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);
}
/**
* 如果minCapacity<0说明溢出了,也就是试图申请超过Integer.MAX_VALUE的元素。
* 否则如果minCapacity介于MAX_ARRAY_SIZE(Integer.MAX_VALUE-8)-Integer.MAX_VALUE之间
* 那么新数组容量为Integer.MAX_VALUE。
*/
private static int hugeCapacity(int minCapacity)
{
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
add方法
/**
* 将元素添加到集合的尾部,也就是添加到其内部持有的数组elementData的最后面。
* 由于添加之前数组可能已经满了(size==capacity),此时没有剩余空间可供使用,就需要扩容。
* 然后在扩容后的数组中添加元素e。
*/
public boolean add(E e)
{
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
/**
* 该方法将元素添加到指定的位置index处。注意凡是会改变数组size的操作都将会递增modCount。这一步在扩容时检查。
* 1、第一步需要先检查传入的index是否合法。合法范围是[0,size]。如果不合法,则抛出IndexOutOfBoundsException.
* 2、添加之前首先要检查是否还有剩余空间,如果没有则先扩容。
* 3、将原数组中的元素从index开始(包括index)。下标为index到size-1这些元素往后移动一位。
* 4、将要插入的元素放在index处。然后递增size。
*/
public void add(int index, E element)
{
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
/**
* 将传入的Collection实现类实例中的所有元素全部添加到集合中。
* 1、该方法首先通过toArray()方法获取传入参数实例中的底层数组的拷贝。
* 2、然后判断该集合内部的数组是否还有足够的剩余空间容纳这些元素,如果不是则扩容。
* 3、将元素复制到经过扩容检查之后的当前list实例的elementData中。
* 4、更新size。如果传入的参数中包含元素返回true,否则返回false。
* 5、当该方法在执行的过程中,如果传入的c发生了修改,那么该方法的执行结果将是不可预知的。
* 解释:
* (1)假如c在修改之前该方法已经执行了c.toArray()。那么我们知道这个方法返回的是原集合中数组
* 的拷贝,那么此时c中所作的更改将不会反映到该集合中。
* (2)如果c在修改时该方法还没有执行c.toArray(),那么等该方法执行到c.toArray()时c中所作的修改
* 将反映到该集合中。
* (3)如果c在修改时该方法正在执行c.toArray(),那么将是最糟糕的。该方法很有可能
* 抛出ArrayIndexOutOfBoundsException异常。这就是该方法执行的不确定性。
*/
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;
}
/**
* 该方法将某一Collection实现类中的所有元素从index处开始插入。
* 1、先检查传入的index是否合理,合理的返回是[0,size]。
* 2、得到c的底层数组的拷贝,这里也会出现上一方法中的不确定性。
* 3、检查集合中elementData是否还有足够空间容纳c中的所有元素,如果不能则扩容。
* 4、将原集合中下标为index到size-1的所有元素向后移动,腾出空间,插入新元素。
* 5、更新size。如果c中不为空那么返回true,否则返回false。
*/
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是否在[0,index]之间。该方法为add()和addAll()服务。
*/
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* 当传入的index不在[0,size]之间的时候(包含size,因为是添加操作),指定的异常信息。
*/
private String outOfBoundsMsg(int index)
{
return "Index: "+index+", Size: "+size;
}
信息类方法
size方法
/**
* 返回数组中当前存放了多少个元素,而不是数组的容量。
*/
public int size()
{
return size;
}
isEmpty方法
/**
* 如何数组中没有元素,那么size为0
*/
public boolean isEmpty()
{
return size == 0;
}
contains方法
/**
* 返回数组中是否包含指定的元素,该方法是通过indexOf(Object o)来确定的。
* 如果能够找到,那么indexOf(0)将返回下标,找不到就返回-1。
*/
public boolean contains(Object o)
{
return indexOf(o) >= 0;
}
toArray方法
/**
* 返回集合元素以数组形式表示的数组,该数组是Object类型,由于java中的泛型的实现采用的是泛型擦除。
* 因为jvm无法在运行时刻获取到list中集合的实际类型。
* 另外可以看到该方法返回的是ArrayList集合内部数组的拷贝,因此返回的数组可以随意的修改而不会影响集合内部的数组。
* 返回的数组中元素的顺序与在集合中的顺序一致。这一点从拷贝的动作也可以看出。
* 该方法连接了基于数组与基于集合的api。
*/
public Object[] toArray()
{
return Arrays.copyOf(elementData, size);
}
/**
* 该方法通过传入一个泛型类型的数组,如果集合中的元素数量小于传入的数组的容量,那么
* 就将集合中的元素复制到传入的数组中,此时如果数组中还有剩余空间,那么数组中第size(集合中的元素的数量)
* 设置为null,以供调用者区分(前提是调用者确定集合中不存在非空元素)。
* 如果传入的数组不能装下集合中的所有元素,那么就新创建一个与传入数组相同类型的新数组,容量为size。
*并将集合中的元素复制到新数组中返回。
*/
@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;
}
查找元素系
indexOf方法
/**
* 返回数组中第一次出现o元素的下标,如果数组中不包含该元素,那么返回-1。
* 如果要查找的元素为null,那么将找到数组中第一个为null的元素,返回其下标。
*/
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相等的元素的下标。如果o为空,那么就是从后往前找到第一个
* 为null的元素。该方法相对于indexOf(Object o)来说是从后往前查找的。
*/
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;
}
get方法
/**
* 获取集合中指定下标的元素,其内部就是获取持有的数组elementData中下标为index的元素。
* 在获取之前需要检查调用者传入的index是否合法。
*/
public E get(int index)
{
rangeCheck(index);
return elementData(index);
}
/**
* 检查调用者传入的下标是否大于等于集合中目前存储的元素数量了。因为合法的index的
* 下标应该是[0,size-1]。
* 该方法没有检查index为负数的时候,这种检查交给了数组本身。
* 别忘了数组本身是可以抛出ArrayIndexOutOfBoundsException。
* 也就是说,如果传入的index>=size那么java开发团队的那帮老头认为应该抛出IndexOutOfBoundsException。
* 而如果是负数那么那帮老头认为应该抛出ArrayIndexOutOfBoundsException。
* 为什么要区分对待呢?都交给数组来检查不行吗?
* 因为ArrayList内部的数组的capacity很多时候是大于size的,这就意味着即便我们传入了index>=size也有可能不会
* 抛出ArrayIndexOutOfBoundsException
*/
private void rangeCheck(int index)
{
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* 直接返回elementData中指定下标出的元素值,并将元素类型强制转型为泛型参数E类型。
*/
E elementData(int index)
{
return (E) elementData[index];
}
修改元素系
set方法
/**
* 该方法首先检查传入的index是否在[0,size-1]范围内,如果不是则抛出RuntimeException.
* 否则首先从elementData中获取到下标为index的元素,然后修改该处的元素值为element,然后返回旧值。
*/
public E set(int index, E element)
{
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
删除元素系
remove方法
/**
* 该方法根据调用者传入的下标删除指定的元素。
* 1、检查传入的下标是否合法。合法的范围[0,size-1]。
* 2、由于要修改size,属于结构化修改。因此要递增modCount。
* 3、获取数组index处的旧元素。
* 4、将index+1到size-1处这些元素前移一位。
* 5、将size处的元素置空,方便垃圾回收,然后递减size,并返回旧值。
*/
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;
}
/**
* 该方法将根据元素来删除集合内与参数元素相等的元素。该方法也可以成为removeFirst。
* 如果传入的参数为null,那么该方法将从前往后查找元素为null的元素并删除。
* 否则将从前往后查找与参数相等的元素并删除。如果删除成功返回true。如果没有找到与之相等的或者也为null的元素
* 那么将不做修改返回false。该方法会修改modCount,具体在fastRemove中进行。
* 为什么不直接使用remove(int index)方法,因为这个删除是在遍历过程中进行,因此下标一定是合理范围内的,所以
* 无需进行范围检查,同时也不需要保存旧值。可见java开发团队在性能与空间上是能省则省。
*/
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;
}
/*
* 跳过类型检查,也无需返回,通过private可知只能由ArrayList内部使用。
*/
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
}
/**
* 删除指定范围的数据,从fromIndex(包含)到toIndex(不包含)。
* 该方法将从toIndex到size-1这些元素前移。然后将移动后空出来的位置置为null,以便尽快垃圾回收。
* 然后修改size的值。
*/
protected void removeRange(int fromIndex, int toIndex)
{
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// clear to let GC do its work
int newSize = size - (toIndex-fromIndex);
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
/**
* 删除该集合中所有与c中的元素相等的元素。该方法要求传入的c不能为null。该方法使用batchRemove方法
* 传入的complement参数为false表示删除所有与c中某一元素相等的元素。
*/
public boolean removeAll(Collection<?> c)
{
Objects.requireNonNull(c);
return batchRemove(c, false);
}
/**
* 删除该集合中所有与c中的元素不相等的元素。该方法要求传入的c不能为null。该方法使用batchRemove方法
* 传入的complement参数为true表示删除所有与c中某一元素不相等的元素,只保留与c中元素相等的元素。
*/
*/
public boolean retainAll(Collection<?> c)
{
Objects.requireNonNull(c);
return batchRemove(c, true);
}
/**
* 该方法执行批量删除操作,如果complement为true,那么保留所有集合元素与c中的某一元素相等的所有元素。
* 如果complement为false,那么就是删去包含c中元素的所有元素。
*/
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
{
// 、如果执行contains比较的过程中无法比较抛出了异常,那么将r后面的元素保留下来。
if (r != size)
{
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size)
{
// 清理掉剩余空间,已帮助gc。
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
clear方法
/**
* 删除集合中的所有元素,由于是结构化修改,首先需要递增modCount。然后将集合中的所有元素置空以便垃圾收集。
* 然后将size置为0.
*/
public void clear()
{
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
/**
* 如果数组的容量大于数组中实际存放的元素的数量,那么该方法可以通过新创建一个容量为元素数量的新数组,然后将
* 元素复制到新数组中,这样elementData就处于满的状态,从而达到了为ArrayList“瘦身”的目的。
* 每次进行结构化的修改,都需要递增modCount,结构化的修改就是数组的size(注意:不是capacity)发生了变化。
*/
public void trimToSize()
{
modCount++;
if (size < elementData.length)
{
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
/**
* 检查数组的容量是否可以容纳些新添加的元素,必要的时候会对数组进行扩容。
*/
public void ensureCapacity(int minCapacity)
{
//如果创建时没有指定任何参数此时数组还没有被初始化,那么minExpand将会为DEFAULT_CAPACITY,也就是10,
//这时minCapacity需要大于10才会触发扩容。
//其他情况也就是数组已经初始化了,那么minCapacity需要大于0才会触发初始化。
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand)
{
ensureExplicitCapacity(minCapacity);
}
}
/**
* 返回ArrayList实例的一个浅复制。所谓浅复制就是集合中存放的元素仍然是相同的,他们指向相同的堆内存地址。
* 此时如果改变复制的ArrayList的元素,那么原先的ArrayList中的元素也将会受到影响。
* 该方法就是新创建一个ArrayList实例。然后复制数组中的元素到新集合实例中的elementData中。由于集合中存放的都是
* 对象引用类型的元素,因此复制的elementData中的元素实际上就是元素的引用。所以这是浅复制。
*/
public Object clone()
{
try
{
ArrayList<?> v = (ArrayList<?>) 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(e);
}
}
/**
* 直接使用Arrays工具类
*/
@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
序列化与反序列化
从ArrayList的操作来看,要自定义序列化规则,只需要重写writeObject方法和readObject方法即可。
/**
* 保存ArrayList实例的状态到流中,该方法用户list的序列化。可以看到ArrayList通过重写了writeObject方法
* 来自定义了序列化规则。该方法要求执行过程中不能进行结构化修改,否则将抛出ConcurrentModificationException,
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException
{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++)
{
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount)
{
throw new ConcurrentModificationException();
}
}
/**
* 从流中重建ArrayList实例。该方法用于反序列化。课件ArrayList自定义了反序列化规则。
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException
{
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0)
{
// be like clone(), allocate array based upon size not capacity
int capacity = calculateCapacity(elementData, size);
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++)
{
a[i] = s.readObject();
}
}
}
ArrayList内部的迭代器实现
/**
* 这是Arraylist内部的Iterator接口的实现类,并且以私有内部类的形式声明仅供ArrayList使用。
* 凡是有expectedModCount的都是支持fail-fast机制的。
*/
private class Itr implements Iterator<E>
{
int cursor; // 下一个要返回元素的下标
int lastRet = -1; // 上一个返回元素的下标。
int expectedModCount = modCount;
Itr() {}
public boolean hasNext()
{
return cursor != size;
}
@SuppressWarnings("unchecked")
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];
}
public void remove()
{
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try
{
//调用remove方法是委托外部的list的remove方法进行的。
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex)
{
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer)
{
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount)
{
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
/**
*检查在过程中有没有被修改过
*/
final void checkForComodification()
{
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
/**
* ListIterator接口的实现类,也是以私有内部类的形式声明并使用,该类相比Itr类具有
* 更多的功能。
*/
private class ListItr extends Itr implements ListIterator<E>
{
ListItr(int index)
{
super();
cursor = index;
}
public boolean hasPrevious()
{
return cursor != 0;
}
public int nextIndex()
{
return cursor;
}
public int previousIndex()
{
return cursor - 1;
}
@SuppressWarnings("unchecked")
public E previous()
{
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i];
}
public void set(E e)
{
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try
{
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex)
{
throw new ConcurrentModificationException();
}
}
public void add(E e)
{
checkForComodification();
try
{
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
}
catch (IndexOutOfBoundsException ex)
{
throw new ConcurrentModificationException();
}
}
}
/**
* 该方法返回一个list iterator来遍历该集合中的elementData数组,开始的位置指定为index。
* 该方法将首先检查index是否在[0,size]之间。
*/
public ListIterator<E> listIterator(int index)
{
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
/**
* 该方法返回一个list iterator来遍历该集合中的elementData数组,默认开始位置从下标为0开始遍历。
*/
public ListIterator<E> listIterator()
{
return new ListItr(0);
}
/**
* 该方法返回一个iterator来遍历该集合中的elementData数组,返回的Itr是Iterator的实现类,该类相比ListItr更
* 简单一些。
*/
public Iterator<E> iterator()
{
return new Itr();
}
/**
* 返回从fromIndex(包含)到toIndex(不包含)之间的所有元素的视图。
* 如果fromIndex==toIndex那么返回的视图为空。注意该方法返回的是一个List接口
* 的实现类SubList,它并不能被转型为ArrayList。
*/
public List<E> subList(int fromIndex, int toIndex)
{
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
/**
* 检查要返回的视图之间的fromIndex与toIndex是否合理。
*/
static void subListRangeCheck(int fromIndex, int toIndex, int size)
{
if (fromIndex < 0)
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
if (toIndex > size)
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex +
") > toIndex(" + toIndex + ")");
}
/**
* 由于SubList也是继承自AbstractList,它与ArrayList的关系更像是同门师兄弟的关系。所有不可以
* 将ArrayList与SubList相互转化。SubList内部持有一个外部的ArrayList的引用,也就是parent
* 对该类实例所做的结构化的修改将反映到原list集合中,因为该类的结构化修改的方法是委托父类的相应方法实现了
* SubList本身并不存储数据。
*/
private class SubList extends AbstractList<E> implements RandomAccess
{
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size;
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex)
{
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
public E set(int index, E e)
{
rangeCheck(index);
checkForComodification();
E oldValue = ArrayList.this.elementData(offset + index);
ArrayList.this.elementData[offset + index] = e;
return oldValue;
}
public E get(int index)
{
rangeCheck(index);
checkForComodification();
return ArrayList.this.elementData(offset + index);
}
public int size()
{
checkForComodification();
return this.size;
}
public void add(int index, E e)
{
rangeCheckForAdd(index);
checkForComodification();
parent.add(parentOffset + index, e);
this.modCount = parent.modCount;
this.size++;
}
public E remove(int index)
{
rangeCheck(index);
checkForComodification();
E result = parent.remove(parentOffset + index);
this.modCount = parent.modCount;
this.size--;
return result;
}
protected void removeRange(int fromIndex, int toIndex)
{
checkForComodification();
parent.removeRange(parentOffset + fromIndex,
parentOffset + toIndex);
this.modCount = parent.modCount;
this.size -= toIndex - fromIndex;
}
public boolean addAll(Collection<? extends E> c)
{
return addAll(this.size, c);
}
public boolean addAll(int index, Collection<? extends E> c)
{
rangeCheckForAdd(index);
int cSize = c.size();
if (cSize==0)
return false;
checkForComodification();
parent.addAll(parentOffset + index, c);
this.modCount = parent.modCount;
this.size += cSize;
return true;
}
public Iterator<E> iterator()
{
return listIterator();
}
public ListIterator<E> listIterator(final int index)
{
checkForComodification();
rangeCheckForAdd(index);
final int offset = this.offset;
return new ListIterator<E>()
{
int cursor = index;
int lastRet = -1;
int expectedModCount = ArrayList.this.modCount;
public boolean hasNext()
{
return cursor != SubList.this.size;
}
@SuppressWarnings("unchecked")
public E next()
{
checkForComodification();
int i = cursor;
if (i >= SubList.this.size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (offset + i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[offset + (lastRet = i)];
}
public boolean hasPrevious()
{
return cursor != 0;
}
@SuppressWarnings("unchecked")
public E previous()
{
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (offset + i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[offset + (lastRet = i)];
}
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer)
{
Objects.requireNonNull(consumer);
final int size = SubList.this.size;
int i = cursor;
if (i >= size)
{
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (offset + i >= elementData.length)
{
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount)
{
consumer.accept((E) elementData[offset + (i++)]);
}
// update once at end of iteration to reduce heap write traffic
lastRet = cursor = i;
checkForComodification();
}
public int nextIndex()
{
return cursor;
}
public int previousIndex()
{
return cursor - 1;
}
public void remove()
{
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try
{
SubList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = ArrayList.this.modCount;
} catch (IndexOutOfBoundsException ex)
{
throw new ConcurrentModificationException();
}
}
public void set(E e)
{
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try
{
ArrayList.this.set(offset + lastRet, e);
} catch (IndexOutOfBoundsException ex)
{
throw new ConcurrentModificationException();
}
}
public void add(E e)
{
checkForComodification();
try
{
int i = cursor;
SubList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = ArrayList.this.modCount;
} catch (IndexOutOfBoundsException ex)
{
throw new ConcurrentModificationException();
}
}
final void checkForComodification()
{
if (expectedModCount != ArrayList.this.modCount)
throw new ConcurrentModificationException();
}
};
}
public List<E> subList(int fromIndex, int toIndex)
{
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, offset, fromIndex, toIndex);
}
private void rangeCheck(int index)
{
if (index < 0 || index >= this.size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private void rangeCheckForAdd(int index)
{
if (index < 0 || index > this.size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private String outOfBoundsMsg(int index)
{
return "Index: "+index+", Size: "+this.size;
}
private void checkForComodification()
{
if (ArrayList.this.modCount != this.modCount)
throw new ConcurrentModificationException();
}
public Spliterator<E> spliterator()
{
checkForComodification();
return new ArrayListSpliterator<E>(ArrayList.this, offset,
offset + this.size, this.modCount);
}
}
java8新增api
/**
* 通过传入相应的Consumer实现类,来决定如何“消费”集合中的所有单个元素。
*/
@Override
public void forEach(Consumer<? super E> action)
{
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
/**
* Spliterator是Java 8中加入的一个新接口;这个名字代表“可拆分迭代器”(splitable iterator)。
* 和Iterator一样,Spliterator也用于遍历数据源中的元素,但它是为了并行执行而设计的。Java 8已经
* 为集合框架中包含的所有数据结构提供了一个默认的Spliterator实现。集合实现了Spliterator接口,接口提供了
* 一个spliterator方法。
*/
@Override
public Spliterator<E> spliterator()
{
return new ArrayListSpliterator<>(this, 0, -1, 0);
}
/**
* 基于索引的一分为二的延迟初始化并行迭代器。
*/
static final class ArrayListSpliterator<E> implements Spliterator<E>
{
/*
* If ArrayLists were immutable, or structurally immutable (no
* adds, removes, etc), we could implement their spliterators
* with Arrays.spliterator. Instead we detect as much
* interference during traversal as practical without
* sacrificing much performance. We rely primarily on
* modCounts. These are not guaranteed to detect concurrency
* violations, and are sometimes overly conservative about
* within-thread interference, but detect enough problems to
* be worthwhile in practice. To carry this out, we (1) lazily
* initialize fence and expectedModCount until the latest
* point that we need to commit to the state we are checking
* against; thus improving precision. (This doesn't apply to
* SubLists, that create spliterators with current non-lazy
* values). (2) We perform only a single
* ConcurrentModificationException check at the end of forEach
* (the most performance-sensitive method). When using forEach
* (as opposed to iterators), we can normally only detect
* interference after actions, not before. Further
* CME-triggering checks apply to all other possible
* violations of assumptions for example null or too-small
* elementData array given its size(), that could only have
* occurred due to interference. This allows the inner loop
* of forEach to run without any further checks, and
* simplifies lambda-resolution. While this does entail a
* number of checks, note that in the common case of
* list.stream().forEach(a), no checks or other computation
* occur anywhere other than inside forEach itself. The other
* less-often-used methods cannot take advantage of most of
* these streamlinings.
*/
private final ArrayList<E> list;
private int index; // current index, modified on advance/split
private int fence; // -1 until used; then one past last index
private int expectedModCount; // initialized when fence set
/** Create new spliterator covering the given range */
ArrayListSpliterator(ArrayList<E> list, int origin, int fence,
int expectedModCount)
{
this.list = list; // OK if null unless traversed
this.index = origin;
this.fence = fence;
this.expectedModCount = expectedModCount;
}
private int getFence()
{ // initialize fence to size on first use
int hi; // (a specialized variant appears in method forEach)
ArrayList<E> lst;
if ((hi = fence) < 0)
{
if ((lst = list) == null)
hi = fence = 0;
else
{
expectedModCount = lst.modCount;
hi = fence = lst.size;
}
}
return hi;
}
public ArrayListSpliterator<E> trySplit()
{
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid) ? null : // divide range in half unless too small
new ArrayListSpliterator<E>(list, lo, index = mid,
expectedModCount);
}
public boolean tryAdvance(Consumer<? super E> action)
{
if (action == null)
throw new NullPointerException();
int hi = getFence(), i = index;
if (i < hi)
{
index = i + 1;
@SuppressWarnings("unchecked") E e = (E)list.elementData[i];
action.accept(e);
if (list.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
return false;
}
public void forEachRemaining(Consumer<? super E> action)
{
int i, hi, mc; // hoist accesses and checks from loop
ArrayList<E> lst; Object[] a;
if (action == null)
throw new NullPointerException();
if ((lst = list) != null && (a = lst.elementData) != null)
{
if ((hi = fence) < 0)
{
mc = lst.modCount;
hi = lst.size;
}
else
mc = expectedModCount;
if ((i = index) >= 0 && (index = hi) <= a.length)
{
for (; i < hi; ++i)
{
@SuppressWarnings("unchecked") E e = (E) a[i];
action.accept(e);
}
if (lst.modCount == mc)
return;
}
}
throw new ConcurrentModificationException();
}
public long estimateSize()
{
return (long) (getFence() - index);
}
public int characteristics()
{
return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
}
}
/**
* Predicate是jdk8新增的接口,根据这个断言实现类的条件来决定是否删除元素。
* 通过断言的test()方法,该方法如果返回true,则代表符合删除条件,可以将其从集合中删除。
*/
@Override
public boolean removeIf(Predicate<? super E> filter)
{
Objects.requireNonNull(filter);
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
//这个用来标记那个元素被删除了。
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++)
{
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
if (filter.test(element)) {
removeSet.set(i);
removeCount++;
}
}
if (modCount != expectedModCount)
{
throw new ConcurrentModificationException();
}
// 将剩余的元素移动以补齐前面的删除元素的空缺。
final boolean anyToRemove = removeCount > 0;
if (anyToRemove)
{
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++)
{
i = removeSet.nextClearBit(i);
elementData[j] = elementData[i];
}
for (int k=newSize; k < size; k++)
{
elementData[k] = null; // Let gc do its work
}
this.size = newSize;
if (modCount != expectedModCount)
{
throw new ConcurrentModificationException();
}
modCount++;
}
return anyToRemove;
}
/**
* UnaryOperator是jdk8新增的接口,通过实现类的apply方法实现单目运算符的作用
* 来决定如何替换list中的元素。
*/
@Override
@SuppressWarnings("unchecked")
public void replaceAll(UnaryOperator<E> operator)
{
Objects.requireNonNull(operator);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++)
{
elementData[i] = operator.apply((E) elementData[i]);
}
if (modCount != expectedModCount)
{
throw new ConcurrentModificationException();
}
modCount++;
}
需要注意的是以下几点:
- 数组中的数组拷贝的操作有的使用Arrays.copyOf方法,有的使用System.arraycopy方法,这个只是api那个方便用哪个。实际上深入copyOf方法可知,该方法内部是调用的System.arraycopy方法,是一个native方法。
- 集合中所有结构化修改,都需要递增modCount。所谓结构化修改简单来说就是操作会改变集合的size或者改变集合内部元素的排序方式了。而modCount是继承自AbstractList。是一个protected transient int 类型的变量。该变量主要用来支持fail-fast机制的。
- 在获取集合的迭代器之后,集合的元素不允许进行结构化修改,之所有通过迭代器可以是因为迭代器的修改操作是委托集合内部的修改操作实现的,并且在实现完成之后相应的同步了expectedModCount与modCount。
- 如果使用无参的构造函数创建ArrayList,那么是采用延迟初始化的方式,在第一次调用add方法的时候,将底层数组扩容为10。
关于Vector
Vector内部也是基于数组实现的动态链表,只不过vector中所有公开的方法都使用了synchronized修饰成为了同步方法,这使得该类中的方法在并发的情况下效率不高,不并发的情况下由于获取锁释放锁的开销也不如ArrayList,因此属于已经不再使用的方法。其内部除了使用synchronized修饰之外,其他与ArrayList实现基本一致。
关于Stack
Stack继承自Vector,因此他也是已经过时的类,它内部的很多方法都是借助与Vector的方法实现的。由于LinkedList内部集成了Stack的的所有的API。因此现在Stack和Deque都推荐使用LinkedList。如果要求多线程下的线程安全性。那么可以使用ConcurrentLinkedDeque代替。这里这两个就不展开了。