/**
* Trims the capacity of this ArrayList instance to be the
* list's current size. An application can use this operation to minimize
* the storage of an ArrayList instance.
*/
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
在ArrayList中,这个方法的作用应该是尽可能的减少数组所占用的内存。当定义的一个ArrayList的容量是100,但实际上里面存储的只有20个数据,那么当调用这个方法的时候,这个ArrayList的容量就是它当前包含的元素数量也就是20.这对于减少内存的占用应该很有用,不然也不会设定这样一个方法。
/**
* Returns true if this list contains the specified element.
* More formally, returns true if and only if this list contains
* at least one element e such that
* (o==null ? e==null : o.equals(e)).
*
* @param o element whose presence in this list is to be tested
* @return true 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 i such that
* (o==null ? get(i)==null : o.equals(get(i))),
* 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;
}
当调用contains方法去检查整个ArrayList里面是否包含某个元素的时候,实际上是使用indexOf方法去检查。indexOf是返回某个元素所在的下标,如果这个元素存在,返回的下标值肯定就不是一个负数,如果不存在,则返回-1这个数。indexOf这个方法也支持null的检查,默认该元素不存在,如果找到该元素,则返回下标。
/**
* Returns a shallow copy of this ArrayList instance. (The
* elements themselves are not copied.)
*
* @return a clone of this ArrayList 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);
}
}
/**
* Returns an array containing all of the elements in this list
* in proper sequence (from first to last element).
*
* 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.
*
*
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);
}
clone方法的返回值是Object对象,也就是说,如果我们当时创建的ArrayList存储的元素是int类型的,那么克隆之后返回的也是存储int类型的ArrayList。并且,这个克隆的工作并不是ArrayList自己完成,而是调用了父级的方法去完成。
和clone方法不同,toArray方法返回的是一个object对象数组,在注释中说明这个方法是“safe”的,也就是说,toArray拿出来的是这个数组的引用,不会实际影响到这个ArrayList。
/**
* 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.
*
* 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
* null. (This is useful in determining the length of the
* list only 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[] 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;
}
/**
* 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) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
return (E) elementData[index];
}
toArray还有一个重载方法,我们设定源数据是一个char类型的ArrayList,而我们设定的是存储int类型的a,如果说a的容量没有源数据大,那么ArrayList会自动帮我们创建一个新的数组来存储。源数据是包含30个元素,而你的a只设定容量为20,那么当你使用这个方法的时候,你的a会将源数据的30个元素添加进来,从原先的20容量,变成了30容量。同样的,如果你a本来设定的容量就比源数据大,那么它会调用System.arraycopy方法。
src |
Object : the source array.
|
srcPos |
int : starting position in the source array.
|
dest |
Object : the destination array.
|
destPos |
int : starting position in the destination data.
|
length |
int : the number of array elements to be copied. |
arraycopy这个方法传递5个参数,第一个参数是不会改动长度大小的数组src,第二个参数是这个src的开始位置,从这个位置开始一直复制;第三个参数是待改动的数组dest,也就是说,因为加入了东西,所以它的长度大小会被改变;第四个参数,是dest的开始位置,要从哪个位置开始;第五个参数是长度,也就是说需要被改变的长度。
ArrayList的get方法也很好理解,就直接返回对应数组下标的元素。
/**
* 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 i such that
* (o==null ? get(i)==null : o.equals(get(i))),
* 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;
}
lastIndexOf方法,获取ArrayList中某个元素最后出现的下标。实现方法也很简单,就是从最后开始检查,直接每一个都遍历一遍。当这个元素并不存在的时候,就返回-1.
/**
* 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) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
E oldValue = (E) elementData[index];
elementData[index] = element;
return oldValue;
}
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return true (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
set方法是有返回值的,当我们对一个ArrayList数组操作的时候,将指定下标的元素先拿出来,然后放入我们指定的元素,然后ArrayList将拿出来的旧的oldVlaue给我们看,你看看,你换出来的是这个东西。我觉得这个是为了节省我们一些操作,比如,如果它不给我们返回值,而我们需要替换出来的这个值。那么我们肯定会先将这个值拿出来存着,然后再去替换,最后再使用这个值去做别的事情。或者说,它将旧oldValue给我们看,是为了确保我们没有换错想换的值,可以在代码中进行后续的判断。
add方法,添加一个元素,如果不指定添加的位置的话,默认就是添加到末尾。而这个方法还有一个重载方法。
/**
* 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) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
这个add方法传递进去两个参数,一个是添加位置的下标,一个是添加进入的元素。它先判断,你要插入的位置是不是超过了我们数组的长度,如果是,就抛出数组越界的错误。如果你插入的位置没毛病,那么ArrayList先自己“扩容 ,也就是容量+1.然后又会用到一个方法System.arraycopy方法。不同的是,这次的源数据和目标数据都是它自己,也就是说,它将插入位置后面的所有元素都复制下来,然后移动一个位置,放在自己的身上。比如,一个长度为4的,内容是1234的数据。我在第二个位置插入一个5。那么它先自己扩容成为1234null,然后复制第二个位置(包含第二个位置)之后的所有数,放到自己里面,也就是往后挪一个位置。此时数据成为了12234。然后将需要加入的元素替换掉,于是成为了15234。
/**
* 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) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
modCount++;
E oldValue = (E) 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方法,移除指定下标位置的元素。如果指定的下标位置超出了数组长度,则抛出数组长度溢出错误。之后统计所有需要往左移动的元素的数量,如果移除的是最后一个位置的元素,那么ArrayList直接将长度缩短。否则,调用System.arraycopy方法,将需要移动的元素copy到自己本身里面。返回被移除的元素的值。
其实就是ArrayList偷个懒,如果移除最后一个位置的元素,那我自己不必动手,交给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
* i such that
* (o==null ? get(i)==null : o.equals(get(i)))
* (if such an element exists). Returns true 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 true 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;
}
/*
* 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
}
/**
* 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;
}
remove还有一个重载的方法,传递的参数是一个对象,当我不知道这个对象在ArrayList哪个位置的时候,我只要传递这个对象进去,那么ArrayList就会帮我们把找到的第一个匹配的对象移除。而移除的方法fastRemove中,也就是说,remove找到相应对象所在的下标,然后将下标传递给fastRemove,让它来做移除的工作。
和之前的按下标移除一样,如果移除的对象是最后一个,那么直接将最后一个设置为null,然后容量大小-1就行了。如果不是,那么将需要往左移的数组复制然后拷贝到自己相应的位置,然后再将容量大小-1.
clear方法,将整个数组list设置为null,然后将长度定义为0即可。剩下的交给垃圾回收GC处理。
/**
* 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 true 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;
}
/**
* 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 true 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) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(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有两个重载方法,一个只需要传递添加的数组;另一个是在指定的下标位置添加数组。
第一个很好理解,先获取你要插入的数组的长度,然后再调用”扩容“方法ensureCapacityInternal,然后再调用System.arraycopy方法来将你的数组添加到原先数组的末尾。
第二个就有点复杂,你指定的下标如果数组越界,那就不处理给你报个错误。如果插入位置的下标没有错误,那我记录你插入数组的长度,然后调用”扩容“方法。再然后,记录我需要移动的数的个数,如果不是0个,则乖乖的挪动,然后将你要添加的数添加进来,然后告诉size变动了。最后就是返回值了,如果你需要添加的这个数组,确确实实的change改动了ArrayList,那么它就会返回true给你,如果你加进来的东西,根本没有改变任何东西,那么它将返回false。
/**
* 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) {
// Android-changed: Throw an IOOBE if toIndex < fromIndex as documented.
// All the other cases (negative indices, or indices greater than the size
// will be thrown by System#arrayCopy.
if (toIndex < fromIndex) {
throw new IndexOutOfBoundsException("toIndex < fromIndex");
}
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;
}
除了单个的移除,ArrayList也可以按照下标进行整段的移除。第一个参数是开始下标,第二个参数是结束下标。比如,一个长度为10,内容为0123456789的数组。当我第一个参数设定为2,第二个参数设定为4的时候,表示,我要把第三个数和第四个数给移除掉。也就是说,移除的内容,包含第一个参数下标索引的元素,但不包括第二个参数下标索引的元素。所以,它移除的是2个元素,分别是第三个元素和第四个元素。当移除之后,长度就变成了8,内容成为了:01456789
如果说,你开始的索引fromIndex小于0或者大于数组长度,那么就会报数组越界的错误。同样的,结束索引如果超过了数组长度,也会报错,以及结束索引比开始索引小也会报错。使用的时候,一定要确保fromIndex大于toIndex。
该方法在运行的时候,会先判断结束索引是否会比开始索引小。然后记录移除的元素个数,然后调用System.arraycopy移动需要移动的元素。然后记录ArrayList新的容量大小size,然后将toIndex后面的所有元素设置为null,然后更改ArrayList的size大小。
还是拿上面那个例子来说明吧。0123456789变成了0145678989,然后又变成了01456789nullnull,然后由于GC垃圾回收机制,将null回收掉了,于是成为了01456789.
/**
* Constructs an IndexOutOfBoundsException detail message.
* Of the many possible refactorings of the error handling code,
* this "outlining" performs best with both server and client VMs.
*/
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
outOfBoundsMsg方法,只在抛出了数组越界异常的时候,告诉我们,你设置的下标index,已经超过了ArrayList数组的size长度。
/**
* 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
* (optional)
* @throws NullPointerException if this list contains a null element and the
* specified collection does not permit null elements
* (optional),
* or if the specified collection is null
* @see Collection#contains(Object)
*/
public boolean removeAll(Collection> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
/**
* Retains only the elements in this list that are contained in the
* specified collection. In other words, removes from this list all
* of its elements that are not contained in the specified collection.
*
* @param c collection containing elements to be retained in 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
* (optional)
* @throws NullPointerException if this list contains a null element and the
* specified collection does not permit null elements
* (optional),
* or if the specified collection is null
* @see Collection#contains(Object)
*/
public boolean retainAll(Collection> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
除了单个元素,下标索引片段这个移除方法,还有种方法是移除所有指定数组removeAll。同时ArrayList还给了一个反向的方法,retainAll方法是只保留你指定的数组。两者差别举个例子就很好的说明了。一个长度为10,内容为0120120120的数组为例子。
当我们调用removeAll方法,并传递01进去,那么这个方法会移除所有的01,之后的数组就变成了:222。
当我们调用retainAll方法,并传递01进去,那么这个方法会保留所有的01,之后的数组就变成了010101。原理和removeAll刚好相反。
既然retainAll方法和removeAll方法实现的原理相反,那么可以单独设定一个方法,根据传递的参数不同,就可以达到不同的效果了。于是这两个方法最后都调用了batchRemove这个方法,在这个方法中,当你想要保留的时候,第二个参数传递true,当你是想移除的时候,第二个参数就传递false。
在调用batchRemove方法之前,会先调用Object.requireNonNull方法来判断,传递进来的c是不是null,如果是null的话,就会抛出NullPointerException异常。