ArrayList源码分析(二)
这里是ArrayList的第二部分,介绍remove、clear、sublist、trimToSize、iterator、toArray等方法。
(多看看源码有利于对集合类使用的理解~)
remove方法
根据下标remove
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;
}
流程如下:
1)判断索引有没有越界
2)自增修改次数
3)将指定位置(index)上的元素保存到oldValue
4)计算得到需要移动的元素数
5)使用System.arraycopy,将需要移动数向前移动一位
6)将最后一个数设为null(准备被垃圾回收器回收)
7)将原来的值oldValue返回
注意:调用这个方法不会缩减数组的长度,只是将最后一个数组元素置空而已。
根据对象remove
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;
}
首先判断移除的对象是否为null,则循环遍历数组,如果某一个下标对应的元素为null则使用fastRemove(index)进行移除,返回true;
如果要被移除的对象不为null,则循环遍历,使用equals方法找到对应下标,也是使用fastRemove(index)进行移除,返回true;
下面介绍一下fastRemove(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
}
fastRemove实际上就是根据下标的remove方法的简化版,没有了返回值,也不需要进行下标是否越界的检查
clear方法
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
先是增加修改次数,然后循环遍历将数组中的每一个元素修改为null,最后将数组的长度修改为0,这样就达到了清除集合内所有元素的目的。
sublist方法
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
先是会对sublist传入的参数进行检查,然后返回一个其内部类sublist的一个实例。在该实例中的每一个方法都有checkForComodification();这是对其修改次数并发的检测。简单来说,如果修改了原始ArrayList的结构(’structurally modified’),自然会导致该SubList对象和原ArrayList对象的modCount不同。
所以,在使用sublist的时候要注意结构性的修改带来的ConcurrentModificationException。
trimToSize方法
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
ArrayList所说没有用的值并不是null,而是ArrayList每次增长会预申请多一点空间,1.5倍+1,而不是两倍
这样就会出现当size() = 1000的时候,ArrayList已经申请了1200空间的情况
trimToSize 的作用只是去掉预留元素位置,就是删除多余的200,改为只申请1000,内存紧张的时候会用到.
iterator方法
public Iterator<E> iterator() {
return new Itr();
}
在ArrayList中实现了iterator迭代器,便于集合的遍历及操作。(具体详见代码或后续对于迭代器的分析)
toArray()方法
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
调用Arrays.copyOf将返回一个数组,数组内容是size个elementData的元素,即拷贝elementData从0至size-1位置的元素到新数组并返回。
toArray(T[] a)方法
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;
}
如果传入数组的长度小于size,返回一个新的数组,大小为size,类型与传入数组相同。所传入数组长度与size相等,则将elementData复制到传入数组中并返回传入的数组。若传入数组长度大于size,除了复制elementData外,还将把返回数组的第size个元素置为空。
后续还会对集合类中的HashMap进行源码分析的文章~