文章目录
一.参数分析
1.elementData
//用来存储的数组的结构
transient Object[] elementData;
2.DEFAULT_CAPACITY
//初始化数组大小
private static final int DEFAULT_CAPACITY = 10;
3.size
//数组大小
private int size;
二.方法分析
1.indexOf
public int indexOf(Object o) {
//1.分成null和非null的情况,两种情况处理方式类似都是循环去获取元素位置
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;
}
//2.如果没有获取到元素位置就返回-1
return -1;
}
2.get
public E get(int index) {
//1.数组下标校验
rangeCheck(index);
//2.拿到该下标元素
return elementData(index);
}
3.set
public E set(int index, E element) {
//1.数组下标校验
rangeCheck(index);
//2.拿到旧值
E oldValue = elementData(index);
//3.替换对应下标元素
elementData[index] = element;
//4.返回旧值
return oldValue;
}
4.add
这边可以看到对modCount进行了+1的操作,修改操作触发
public boolean add(E e) {
//1.添加数组操作,这边会对modCount这个参数+1,修改操作触发
ensureCapacityInternal(size + 1); // Increments modCount!!
//2.赋值
elementData[size++] = e;
return true;
}
5.remove
remove方法这边有一个置空操作,使GC知道该位置空了,可以回收
public E remove(int index) {
//1.判断下标
rangeCheck(index);
//2.删除操作触发.modCount+1
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
//3.native方法把数组元素位置前移
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//4.清空最后一个位置
elementData[--size] = null; // clear to let GC do its work
//5.返回旧值
return oldValue;
}
6.clear
置空数组所有位置
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
7.retainAll和removeAll
retainAll和removeAll都调用了同一个方法:batchRemove,不过入参不一样.
retainAll传true会把包含的元素添加到新数组,removeAll传false会把不包含的元素添加到新数组
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
//1.循环所有元素,retainAll传true,会把c包含的元素复制到新的数组,
// removeALl传false,会把c不包含的元素复制到新数组
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
//和Collection保持一致,好像没什么用
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
//2.判断元素是否减少,循环置空.
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;
}
8.forEach和fail-fast
ArrayList不是线程安全的也是体现在这边,如果多个线程修改同一份数组数据,这边就会出现问题,所以选择快速失败.
- 假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生fail-fast机制。
@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;
//1.这边设置快速失败的标记就是,期望的modCount和实际的modCount值是否一样,如果不一样会退出循环
//2.导致不一样的原因就是在循环的过程中进行了modCount++操作,也就是一系列更改操作
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
//3.抛出错误,结束
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
9.sort
sort直接使用Arrays.sort方法,采用归并排序,这边不详细介绍,详见:Arrays源码阅读
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
//1.使用归并排序
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}