上篇分析到了add()/addAll()方法,接下来继续分析一下其他的核心方法
首先来分析一下删除数据方法:remove(int index)/remove(Object o)
//用于判断下标是否越界
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
public E remove(int index) {
//先进行判断index是否越界
rangeCheck(index);
//删除会引起modCount的增加,具体作用在上篇介绍过了
modCount++;
//记录要被删除的数据
E oldValue = elementData(index);
//后面元素要移动的长度
int numMoved = size - index - 1;
//>0也就是不在末尾的情况,将elementData数组从index+1的位置开始拷贝到elementData中index位置开 始,长度为numMoved的数据
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//将末尾置为null方便GC
elementData[--size] = null; // clear to let GC do its work
//返回被删除的数据
return oldValue;
}
//删除指定元素的删除方法,先找到索引位置在删除
public boolean remove(Object o) {
//首先会判断要删除的元素是否为Null,比较特殊用==
if (o == null) {
//循环遍历寻找元素为null的位置Index,调用fastRemove方法
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
//不为空则用equals循环比较找到
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
//删除成功
return true;
}
}
//没有这个数据则返回false
return false;
}
//快速删除的一个方法,不会返回被删除的值,也不用判断是否下标越界,因为是根据index找到的再调用这个方法,则可以保证不会下标越界,这样可以提升一定的效率
private void fastRemove(int index) {
//删除会引起modCount变量的增加
modCount++;
int numMoved = size - index - 1;
//不在末尾的情况
if (numMoved > 0)
//将elementData数组index+1的位置开始复制到index开始的位置,长度为numMoved
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//方便GC
elementData[--size] = null; // clear to let GC do its work
}
上面的删除方法是删除单个元素的情况, Arraylist还提供了删除范围元素的情况
protected void removeRange(int fromIndex, int toIndex) {
//不多解释
modCount++;
//删除之后,后面的元素要移动的长度
int numMoved = size - toIndex;
//将elementData数组从toIndex开始拷贝到fromIndex开始,长度为numMoved
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// 新数组长度
int newSize = size - (toIndex-fromIndex);
//将新数组长度之后的数据置为null,方便GC
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
//容量大小变为newSize
size = newSize;
}
//删除指定集合中的所有元素
public boolean removeAll(Collection<?> c) {
//判断是否为null,null会抛异常
Objects.requireNonNull(c);
//调用方法
return batchRemove(c, false);
}
/**
c:指定的集合
complment:boolean类型
true的时候删除不包含在c中的元素,也就是交集
false的时候删除包含在c集合中的元素,也就是差集
@return:boolean,是否删除成功
*/
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
//r:读指针 w:写指针
int r = 0, w = 0;
//用于判断是否修改
boolean modified = false;
try {
for (; r < size; r++)
//指定集合c中如果包含elementData[r]而且complement==false,
elementData[w++]存放的就是出去c中有的元素
//为true的话则存放二者的交集
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
//正常结束的话r==size,否则会抛出异常
if (r != size) {
//将elementData数组从r位置开始拷贝到w位置开始,长度为size-r
System.arraycopy(elementData, r,
elementData, w,
size - r);
//更新一个还剩多少数据
w += size - r;
}
//如果w==size则表示保留了全部数据,没有发生修改
if (w != size) {
//将数组从w位置之后的数据指向null,方便为gc
for (int i = w; i < size; i++)
elementData[i] = null;
//修改的次数
modCount += size - w;
//size更新为w
size = w;
//修改成功返回true
modified = true;
}
}
//返回是否修改成功
return modified;
}
//删除全部元素
public void clear() {
modCount++;
//全部删除,置为null被GC
for (int i = 0; i < size; i++)
elementData[i] = null;
//容量变为了0
size = 0;
}
接下来是get()/set()方法
//获取
public E get(int index) {
//判断是否下标越界
rangeCheck(index);
//底层结构依旧是数组,直接根据索引返回值
return elementData(index);
}
//设置更新数据
public E set(int index, E element) {
//判断是否越界
rangeCheck(index);
//存储旧数据
E oldValue = elementData(index);
//更新Index位置上的数据
elementData[index] = element;
//返回修改之前的数据
return oldValue;
}
其他的一些方法解释
//实例序列化的方法
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
//定义一个期望modCount,防止序列化期间被修改
int expectedModCount = modCount;
//默认的序列化方法,但transient/static修饰的属性不会被序列化
s.defaultWriteObject();
//写入容量大小
s.writeInt(size);
//按序写出数据
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
//如果modCount发生了改变,会抛出并发修改异常
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
//从反序列化中重构实例
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
//elementData初始化等于空数组
elementData = EMPTY_ELEMENTDATA;
//默认反序列方法,读入非transient/static修饰属性
s.defaultReadObject();
//读入集合长度,可以忽略
s.readInt(); //ignore
//如果容量>0
if (size > 0) {
//计算需要的最小容量,详细见上篇
int capacity = calculateCapacity(elementData, size);
//检查集合的类型/大小
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
//检查是否需要扩容
ensureCapacityInternal(size);
//定义数组
Object[] a = elementData;
//按顺序读取数据存入a数组
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
ArrayList就介绍到这吧,其他的方法如迭代方法/isEmpty()等等方法有兴趣的可以自行了解。感谢欣赏!