public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
RandomAccess, Cloneable, java.io.Serializable三者是三个ArrayList所实现的接口
RandomAccess 是一个标志接口,表明ArrayList是支持随机访问策略的,同时,官网还特意说明了,如果是实现了这个接口的 List,那么使用for循环的方式获取数据会优于用迭代器获取数据(也就是说for循环比增强for循环的查询效率更高)。
Cloneable也是一个标志接口,表明支持原型模式,实现此接口的类则需要去重写Object的clone()方法;
Serializable还是一个标志接口,表明支持序列化,可以将对象存储在存储介质中,也可以以字节流的形式在网络中进行传输。
接下来来看ArrayList的参数:
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
其中DEFAULT_CAPACITY就是所指定的默认容量,当你使用无参构造的时候将会自动使用该参数作为数组初始长度。
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
空对象数组,主要用于将该引用传递给想要创建容量为空的数组的引用,在jdk1.8后才这么用,而在1.7的时候还是行使DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的功能的,即1.8在容量为空的时候是把EMPTY_ELEMENTDATA的引用传值给elementData,而1.7则是直接创建一个空的数组,增大了开销。
/**
* 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 = {};
直译该数组名是默认容量空元素数据,使用无参构造时候就是让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
由注释来翻译其作用大概是:该数组用于缓存ArrayList要存储的数据。
数组的长度就是ArrayList的容量,空的ArrayList的elementData就直接引用DEFAULTCAPACITY_EMPTY_ELEMENTDATA.
当第一个元素加入的时候就会去自动扩容成DEFAULT_CAPACITY;
注意: transient用来表示一个域不是该对象序行化的一部分,当一个对象被序行化的时候,transient修饰的变量的值是不包括在序行化的表示中的。然而我们从ArrayList实现的接口中可以明显看到该类是可以被序列化的,如果不序列化elementData,那么当反序列化的时候是不是会丢失里面的元素呢?答案是否定的,具体的会涉及到ArrayList的writeObject()和readObject()方法,从而只需要序列化存在的对象,而不用序列化整个数组,节省了时间和空间。
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
无需赘述,该属性可做到见名知意,就是返回ArrayList的长度,注意是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) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
第一个构造函数带有一个int型的参数initialCapacity,翻译成中文就是初始化的容量,就是用于构造一个有初始长度的数组。其代码具体实现也很简单,当参数>0的时候,就让elementData指向一个和参数值相等大小的对象数组,若是参数等于零,就指向上文所介绍的EMPTY_ELEMENTDATA,而不是再去创建一个空数组;如果小于零就抛出异常,因为数组的长度不可能为负数;
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_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();
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;
}
}
简单翻译一下注释的意思就是构造一个包含指定集合的所有元素的list,它们是靠集合的迭代器所返回的。
具体代码解读:先把集合通过toArray()转换成一个对象数组,然后判断数组的长度是不是等于0,如果等于0的话,就让elementData指向EMPTY_ELEMENTDATA,说明所传的就是一个空集合,如果不为0还要进行一次判断,elementData.getClass(),用反射的方式来获取类名.wtf,这难倒是脱裤子放屁?显然不是,不同类实现toArray()的返回值不一定是Object[].class,所以要防止这种情况。当这种情况出现时,就用Arrays.copyOf把它放入一个新创建的Object[]数组,让elementData指向的始终是Object数组;
参数和构造函数就是以上这些,具体API解读等空的时候再补上;
/**
* 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 = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
由该方法名大致可明白它的作用就是把数组修剪成size的大小,modCount是由ArrayList所继承的AbstractList的一个参数,它的具体作用我copy了一段他人的的翻译:
该字段表示list结构上被修改的次数。结构上的修改指的是那些改变了list的长度大小或者使得遍历过程中产生不正确的结果的其它方式。
该字段被Iterator以及ListIterator的实现类所使用,如果该值被意外更改,Iterator或者ListIterator 将抛出ConcurrentModificationException异常,
这是jdk在面对迭代遍历的时候为了避免不确定性而采取的快速失败原则。
子类对此字段的使用是可选的,如果子类希望支持快速失败,只需要覆盖该字段相关的所有方法即可。单线程调用不能添加删除terator正在遍历的对象,
否则将可能抛出ConcurrentModificationException异常,如果子类不希望支持快速失败,该字段可以直接忽略
原文链接:https://www.cnblogs.com/nevermorewang/p/7808197.html
说白了就是为迭代器所用的,和其他代码关联不大。而剩下的代码就是判断数组elementData的长度是否大于ArrayList的元素的个数,如果大于就进行缩容。
/**
* 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 != 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);
}
}
ensureCapacity直译成中文就是保证容量的意思,笔者翻译这段注释的大致意思是:当必要的时候就增加ArrayList的容量来保证它能够至少承受的住由最小容量参数所指定的数量,而minCapacity就是所期望的最小容量。而minExpand顾名思义就是最小扩展的长度。当elementData不是初始的空数组的时候就让最小需要扩展为0,因为此时的数组是已经扩容过的,不是初次扩容,而若是初次扩容,则它的初始值只能是大于默认长度的。当初次扩容的量小于默认初始值的时候,是不进行扩容的,如果满足了,再来看看扩容的具体实现方法ensureExplicitCapacity(minCapacity);
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
也很简单,就是当期望的最小容量大于存储元素的数组的长度的时候才会进入下一步真正扩容的具体实现了。否则的话你要扩容结果期待值比现在的容量还少,那岂不是很有可能要报IndexOutOfBoundsException了,总之就是很不合逻辑。接下来看看最关键的一步,grow(minCapcity);
/**
* 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);
}
该段注释与ensureCapacity一样,可见是其具体实现。而代码也相对简单,我来具体解读一下:
oldCapacity就是扩容前elementData的长度,而newCapacity就是扩容后的长度,">>n"表示右移,相当于除以2^n;而>>1就表示右移1位,相当于除以2了,此处为什么用移位而不是用/2或者直接表示1.5oldCapacity呢,主要是一来省去数据类型转换(int 强转为 double),二来移位是机器底层的操作,效率更高。
接下来的判断是当扩容1.5倍之后的容量比最小扩容量小的话,就把最小扩容量赋予newCapacity,如果扩容后的容量大于了数组所能承受的最大容量
MAX_ARRAY_SIZE,(值为Integer.MAX_VALUE - 8),的时候,就把下一个方法hugeCapacity(minCapacity)的值赋予newCapacity。至于为什么MAX_ARRAY_SIZE的值要取Integer的最大值-8呢,这就涉及JVM的知识了,因为数组的对象头信息一般不超过8个字节。
下一个判断所涉及的情况又有多个可能,详情见hugeCapacity(minCapacity);
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小于0,就直接报错,这个参数有问题。如果当minCapacity > MAX_ARRAY_SIZE的时候,就让minCapacity为Integer.MAX_VALUE,否则就是
MAX_ARRAY_SIZE,之后就对数组进行了数组复制并扩容。
注意:此处我发现它竟然把Integer.MAX_VALUE赋予了数组的长度???难倒不是MAX_ARRAY_SIZE么,我特意去查了一下,最后在知乎上找到了答案:
所以,最大长度是Integer.MAX_VALUE而不是MAX_ARRAY_SIZE,切记切记。
/**
* Returns the number of elements in this list.
*
* @return the number of elements in this list
*/
public int size() {
return size;
}
/**
* Returns <tt>true</tt> if this list contains no elements.
*
* @return <tt>true</tt> if this list contains no elements
*/
public boolean isEmpty() {
return size == 0;
}
这两个方法就不再赘述了,一个返回元素的个数,一个判断元素的个数是否为0;
/**
* Returns <tt>true</tt> if this list contains the specified element.
* More formally, returns <tt>true</tt> if and only if this list contains
* at least one element <tt>e</tt> such that
* <tt>(o==null ? e==null : o.equals(e))</tt>.
*
* @param o element whose presence in this list is to be tested
* @return <tt>true</tt> if this list contains the specified element
*/
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
/**
* 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;
}
首先解读一下注释:当list包含所指定的元素的时候返回true。
更正式一点说:当且仅当list包含至少一个元素e满足(onull?enull:o.equals(e))的时候返回true;它的代码就是返回indexOf(o)>=的值;indexOf()的代码其实很简单,它的注释就说的明明白白:返回第一个存在的该元素的索引,也就是说如果你一个ArrayList里有很多个同一元素,但最终返回的还是第一个。如果没有就返回-1,这就是为什么当indexOf()为-1的时候,contains()返回false的原因了。
注意:除了indexOf之外,还有lastIndexOf(),其实就是在遍历比较o与elementData[i]时将其顺序反了一下而已,这个很好理解,我就不上代码了。
/**
* 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 {
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);
}
}
这个方法是重写了Object的clone()方法,必须要实现Cloneable才可以生效。它的注释对其解释是:返回一个ArrayList的浅拷贝实例。浅拷贝就是指不拷贝实例里的元素,而是将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用,而深拷贝却是是实实在在的把原对象的各项属性的值赋予了新对象,但是他们的引用是不同的。
代码解读:调用Object的clone();返回了ArrayList,之后初始化新的ArrayList对象的elementData,重置其modCount;返回这个ArrayList,当没有实现Cloneable的时候抛出异常;
/**
* 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);
}
以正确的顺序(从头到尾)返回一个包含所有list元素的数组,这个方法在数组和结合之间起到了桥梁的作用。说了一大堆,其实就是做一次数组复制,把所有的元素复制到一个大小为list的size的数组中去。
/**
* Returns an array containing all of the elements in this list in proper
* sequence (from first to last element); the runtime type of the returned
* array is that of the specified array. If the list fits in the
* specified array, it is returned therein. Otherwise, a new array is
* allocated with the runtime type of the specified array and the size of
* this list.
*
* <p>If the list fits in the specified array with room to spare
* (i.e., the array has more elements than the list), the element in
* the array immediately following the end of the collection is set to
* <tt>null</tt>. (This is useful in determining the length of the
* list <i>only</i> if the caller knows that the list does not contain
* any null elements.)
*
* @param a the array into which the elements of the list are to
* be stored, if it is big enough; otherwise, a new array of the
* same runtime type is allocated for this purpose.
* @return an array containing the elements of the list
* @throws ArrayStoreException if the runtime type of the specified array
* is not a supertype of the runtime type of every element in
* this list
* @throws NullPointerException if the specified array is null
*/
@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;
}
将要获得的数组填充入指定的a;如果容量足够,就无需扩容,容量不够,就要让它成为一个容量更大的数据,赋值后并返回。
// Positional Access Operations
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) 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);
}
这是List顶层接口所定义的通用方法,此处的具体实现为先对index的返回进行检查,看看是不是超过了最大值,如果超过就抛出IndexOutOfBoundsException,否则就返回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;
}
先检查范围,再获取原先element[index]的值,将新的值赋予elementData[index]后返回原先的值。
/**
* 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;
}
添加元素的功能:注释翻译为把指定的元素添加到list的末端,ensureCapcityInternal就是内部扩容,由于add是添加一个元素,所以add扩容后的大小为size+1;然后末尾新增的元素就是e;至于内部扩容是如何实现的,就看下面的代码;
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
这是两断确保内部容量的代码,其实解读起来很简单,calculateCapacity就是判断elementData是不是初始的空数组,如果是的话就比较minCapacity和默认容量10的大小,如果不是初次扩容,就返回minCapacity;之后如果minCapacity要比当前的elementData数组大的话就进行扩容,否则就不做任何操作;
/**
* Inserts the specified element at the specified position in this
* list. Shifts the element currently at that position (if any) and
* any subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
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++;
}
add(int index,E element)为在指定的索引处插入元素;代码分析:
rangeCheckForAdd(index);检查范围,不必多说,ensureCapacityInternal(int minCapacity)在上文也接触过,无需赘述;System.arraycopy是一个native方法;其中的解释:
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
//代码解释:
//Object src : 原数组
//int srcPos : 从元数据的起始位置开始
//Object dest : 目标数组
//int destPos : 目标数组的开始起始位置
//int length : 要copy的数组的长度
也就是说是从elementData的index及其后面的每个元素放入后面一位,然后index处插入想插入的参数;
/**
* 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;
}
remove(int index):删除指定位置的元素并把然和(被删元素)之后的元素向左移位;
源码解读:
先检查index是否在范围之内;
modCount++;主要是用迭代器遍历的时候不能进行remove操作,否则报错;
numMoved:被删除的元素之后需要移位的元素个数;
如果numMoved=0说明已经是最后一位了,无需再移动了;
之后就是移位了,把被删数组后的数组前移,将数组最后一位设置为null,这样GC就会回收这个没有引用指向的对象了。
/**
* Removes the first occurrence of the specified element from this list,
* if it is present. If the list does not contain the element, it is
* unchanged. More formally, removes the element with the lowest index
* <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
* (if such an element exists). Returns <tt>true</tt> if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
* @param o element to be removed from this list, if present
* @return <tt>true</tt> if this list contained the specified element
*/
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;
}
remove(Object o):删除第一个存在的元素并返回true,如果不存在就返回false;
源码解读:
当o==null的时候,遍历elementData,如果存在元素为null,进行fastRemove(index)操作,最后返回true;如果o不为null则去用equals()来找与o等价的元素,如果找到了就执行fastRemove(index)操作并返回true;如果都没找到最后则返回true;
fastRemove(int index)源码:
/*
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
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)是一样的,只不过不用返回被删除的值而已;
/**
* Removes all of the elements from this list. The list will
* be empty after this call returns.
*/
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
clear():很清楚明了,就是清空ArrayList和elementData数组;代码解读:
modCount++表明是改变元素数量的操作,下面是遍历清空数组的操作,再把size设置为0,表明ArrayList的数组元素数为0;
/**
* Appends all of the elements in the specified collection to the end of
* this list, in the order that they are returned by the
* specified collection's Iterator. The behavior of this operation is
* undefined if the specified collection is modified while the operation
* is in progress. (This implies that the behavior of this call is
* undefined if the specified collection is this list, and this
* list is nonempty.)
*
* @param c collection containing elements to be added to this list
* @return <tt>true</tt> if this list changed as a result of the call
* @throws NullPointerException if the specified collection is null
*/
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;
}
addAll(Collection<? extends E> c):把所有指定集合的元素添加到list的末端,它们按照是通过集合的迭代器得出的顺序所排列的,如果该集合正在被操作那么该操作的行为就是未定义的。(大致意思说就是如果被all的集合此时正在被操作(增删改)的话,那么就不能用这个方法);
代码解读:
首先把要添加的集合转换为数组,然后numNew就等于该集合的长度,对原数组进行一次ensureCapacityInternal()操作;
操作过后的将原数组a从索引0开始添加到目标数组elementData之中,从下标size处开始添加,添加的长度为numNew;
之后让size加上集合的长度,相当于多添加的元素量;返回值时boolean,如果新添加的集合是空集合则返回false,如果不是就返回true;
/**
* Inserts all of the elements in the specified collection into this
* list, starting at the specified position. Shifts the element
* currently at that position (if any) and any subsequent elements to
* the right (increases their indices). The new elements will appear
* in the list in the order that they are returned by the
* specified collection's iterator.
*
* @param index index at which to insert the first element from the
* specified collection
* @param c collection containing elements to be added to this list
* @return <tt>true</tt> if this list changed as a result of the call
* @throws IndexOutOfBoundsException {@inheritDoc}
* @throws NullPointerException if the specified collection is null
*/
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;
}
addAll(int index, Collection<? extends E> c):这个可以类比两个all方法,此处不再赘述。
/**
* Removes from this list all of the elements whose index is between
* {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
* Shifts any succeeding elements to the left (reduces their index).
* This call shortens the list by {@code (toIndex - fromIndex)} elements.
* (If {@code toIndex==fromIndex}, this operation has no effect.)
*
* @throws IndexOutOfBoundsException if {@code fromIndex} or
* {@code toIndex} is out of range
* ({@code fromIndex < 0 ||
* fromIndex >= size() ||
* toIndex > size() ||
* toIndex < fromIndex})
*/
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;
}
removeRange(int fromIndex,int toIndex):方法名可以理解为删除指定范围的数
源码解读:
- numMoved其实就是toIndex及其之后没被删除的元素,把他们填充到fromIndex之后的数组;(此处应当注意fromIndex是被覆盖了,而toIndex还是被保留的,也就是说左边是闭区间,右边是开区间)。
- 之后的newSize就是原来list的元素数量减去缩容去除的元素数,然后将其之后的数组全部重置为null,因为这些元素已经全部前移了。
- 把size更新为newSize,也就是说list的元素数量的更新。
/**
* Removes from this list all of its elements that are contained in the
* specified collection.
*
* @param c collection containing elements to be removed from this list
* @return {@code true} if this list changed as a result of the call
* @throws ClassCastException if the class of an element of this list
* is incompatible with the specified collection
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if this list contains a null element and the
* specified collection does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see Collection#contains(Object)
*/
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
removeAll(Collection<?> c):把所有c中的元素删除;
代码解读:先是判断c是不是null,如果是null就抛出空指针异常,如果不是就返回batchRemove(c,false);的值;
batchRemove代码如下:
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;
}
代码解读:
final Object[] elementData = this.elementData不是很清楚为什么要有这一步,最终操作的不是还是原数组么。。。如果有大佬看到希望能帮忙解答一下;之后就是对elementData数组进行遍历比较,如果该元素存在于c之中,就使得w处的值为该值并自增;这里的w就是写入(write)的意思,r就是读取的意思(read);也就是说其实这段代码的本质就是遍历读取elementData,如果读到的数是不存在的,就把它写入,如果是存在的,就不写入,这样最后写入的就都是不存在在c中的;
try{}catch存在的原因他也已经说明,因为当contains执行的时候是有可能抛出异常的,所以当异常发生的时候r!= size的时候说明是还没有读完的,但是这时候已经读不下去了,默认就是把之后的元素写入(其实是没判断过的),就相当于默认它不存在于c中,所以不用移除了;之后的判断w!=size所执行的就是把移除后多出来的那些元素进行清除了;如果执行了该步,那么要把size也调整成真正写入的元素个数,把modified改为true表明是有元素被清除的,反之则未有被清除的元素。
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
这个就完全是把removeAll的false改成了true,说明只要包含在c中的元素,其他的都不要,说白了就是上面集合的差集;
/**
* Save the state of the <tt>ArrayList</tt> instance to a stream (that
* is, serialize it).
*
* @serialData The length of the array backing the <tt>ArrayList</tt>
* instance is emitted (int), followed by all of its elements
* (each an <tt>Object</tt>) in the proper order.
*/
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实例的状态(这就是所谓的序列化);
代码解读:
expectedModCount就是记录进行I/O之前的modCount,最后还要比较一次防止在多线程过程中出现在I/O的过程中有对数据进行操作的存在;
s.defaultWriteObject();就是把除了transient注解的ArrayList其他属性都序列化
s.writeInt(size);这步我刚开始没看懂,明明上面的private int size已经在上一步被序列化了,这里为什么还要再写入一次呢?后来网上查了一下主要是为了兼容性考虑,主要是因为旧版本的JDK中,ArrayList的实现有所不同,会对length字段进行序列化。
而新版的JDK中,对优化了ArrayList的实现,不再序列化length字段。
这个时候,如果去掉s.writeInt(size),那么新版本JDK序列化的对象,在旧版本中就无法正确读取,因为缺少了length字段。因此这种写法看起来多此一举,实际上却保证了兼容性。
至于下面这个循环就是把每个非空元素写入,这样就无需写入为null的数组元素从而浪费时间与空间。
/**
* Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
* deserialize it).
*/
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();
}
}
}
反序列化的代码:讲从文件中读取的对象写入内存
源码分析:
首先让elementData等于原来的空数组,相当于执行了一次initCapacity为0的构造过程;
s.defaultReadObject();读取所有属性信息;
// Read in capacity
s.readInt(); // ignored 再次读取size,它有个注释ignored,其实就是为了兼容性,可以忽略的一段代码;
if里面就是具体反序列化每个元素的过程了,不再赘述
/**
* Returns a list iterator over the elements in this list (in proper
* sequence), starting at the specified position in the list.
* The specified index indicates the first element that would be
* returned by an initial call to {@link ListIterator#next next}.
* An initial call to {@link ListIterator#previous previous} would
* return the element with the specified index minus one.
*
* <p>The returned list iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
返回一个list元素都在其中的list迭代器(以一个正确的顺序),在list的指定位置开始。指定索引所表示的元素将会成为第一个被调用{@link ListIterator#next next}所返回的结果;调用{@link ListIterator#previous previous}将会返回指定索引往前一个的结果;
它返回的ListItr(index)是一个它的私有内部类;
/**
* An optimized version of AbstractList.ListItr
*/
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();
}
}
}
其注释为对AbstractList的一种优化,其实本质上就是一条链表,而链表存储的数据是index索引;上述的cursor是其父类Itr的参数int cursor; // index of next element to return,主要就是用来指向下一个返回元素的节点;
ListItr(int index):构造函数,主要就是把index赋予cursor;
public boolean hasPrevious():是否有前驱,如果cursor=0,说明是第一个索引,返回false,如果不是0则返回true;
public int nextIndex():返回的是下一个索引,即cursor本身,上述对其注释已说明;
public int previousIndex():返回前驱索引,即当前这个索引;
public E previous():返回当前节点,即前驱:其代码首先就是比较modCount和expectedModCount,如果不相等就快速失败;然后指明i就是cursor-1,即迭代器当前所指的值,对其值的范围验证过后让cursor指向i,lastRet = i,并返回elementData[i]的值;
感觉后面的迭代器什么的已经有点太多且重复了,就不写了。