ArrayList底层分析
//唯一标识判断为同一对象
private static final long serialVersionUID = 8683452581122892189L;
//定义初始容量大小
private static final int DEFAULT_CAPACITY = 10;
//定义一个空的数组,在无参构造中使用
private static final Object[] EMPTY_ELEMENTDATA = {};
//默认空元素数据
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//使用transient使其不会被序列化
elementData是一个缓存数组,它通常会预留一些容量,等容量不足时再扩容,假如实际有了5个元素,但真正存在的容量大小可能为10,那么在序列化时只需要储存5个元素,数组中的最后五个元素是没有实际意义的,不需要储存
transient Object[] elementData;
定义的数组大小
private int size;
用来计数
protected transient int modCount = 0;
ArrayList最大扩容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
内部构造方法
//有参构造initialCapacity,当initialCapacity大于0,则elementData数组大小就等于initialCapacity,如果等于0,则等于默认空数组,如果小于0,报错
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);
}
}
//无参构造,elementData等于默认空元素的数据
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/*有参构造
elementData=c.toArray();先传入的集合转为数组在复制给elementData
size = elementData.length) != 0 现在数组长度size赋值在判断数组的长度是否等于0,
elementData.getClass() != Object[].class 判断类型是否相同,因为不同的类通过toArray方法返回的不一定是Object类型(如下),这样会导致在调用ArrayList增加Object元素的方法的时候出现错误。如果为true的话及调用Arrays.copyOf方法来实现把类型变为Object,如果size==0,则elementData是空数组
*/
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
}
内存修减
/*将集合的容量修建为列表当前大小,可以使用此操作俩减小集合的存储,如果size小于传入列表的长度进行判断,如果列表长度为0则为默认空数组,如果不为空,则进行数组复制建立新的数组并将其赋值给elementData*/
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
扩容规则
/*以oldCapacity + (oldCapacity >> 1);规则进行扩容,扩容大小在1.5倍。如果扩容后的长度小于目前最小长度,则直接等于最小长度,如果大于MAX_ARRAY_SIZE(最大扩容长度)执行扩容
hugeCapacity(如果目前长度小于0抛出异常,如大于了MAX_ARRAY_SIZE,则变为更大长度)*/
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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}1
传值扩容判断
/*如果目前长度大于集合的的最大长度进行扩容*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
对列表进行扩容,判断最小长度,并进行相应大小扩容
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
返回列表长度
public int size() {
return size;
}
判断是否为空,为空则返回0
public boolean isEmpty() {
return size == 0;
}
查找元素是否包含在数组中的索引
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;
}
判断索引是否大于0.如果大于0,则数据存在
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
看是否输入元素在列表的最后
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;
}
进行列表克隆
对原列表进行复制,然后重新装入elementData
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类型的数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
将数组转为对象泛型形数组
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;
}
查询时集合输入位置进行查询,进行查询看是否超出集合的长度,如果超过,抛出异常
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
返回当前位置的元素
E elementData(int index) {
return (E) elementData[index];
}
根据索引返还元素
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
向list装入新的元素
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
根据泛型对象向集合中添加元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
根据泛型对象按某个位置顺序向集合中添加元素
public void add(int index, E element) {
//判断集合的范围值
rangeCheckForAdd(index);
//将集合进行+1扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//对数组进行复制扩容
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//将添加的位置元素进行替换,并重新赋值扩容
elementData[index] = element;
size++;
}
集合对特定位置元素进行移除
public E remove(int index) {
//先对集合长度进行判断
rangeCheck(index);
对初始序列化值进行重新赋值
modCount++;
fail-fast机制,防止多线程下使用ArrayList的办法获取要删除位置的元素
E oldValue = elementData(index);
//需要挪动的元素数量
int numMoved = size - index - 1;
//向前挪动所有的元素
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;
}
用于相等的元素对象删除的方法,传入参数表示对象元素(ArraylList支持nill的传值取值)
当传入元素为null时,则会从数组对象elementData的第一个开始遍历,一直遍历到元素总数size-1处,每一轮迭代都会判断下标对应值是否为null,此处使用==作为null的判断,。应该equal无法作为null的比较
public boolean remove(Object o) {
当传入的元素为null时,则会从数组对象中找到第一个为null时,调用fastRemove()方法进行删除元素,并且结束遍历,整个remove(Object)方法会返回true
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
如果遍历结束,没有在elementData中找到任何的null元素,remote(Object)方法会返回false,表示删除元素失败
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
一、传入的元素为null
当传入的元素为null时,则会从数组对象elementData的第1个元素开始遍历,一直遍历到元素总数size-1的下标处,每1轮的迭代会判断elementData对应下标的元素值是否为null,此处使用==作为元素相等的判断依据,因为null无法使用equals()方法比较对象是否相等
1、当从ArrayList对象持有的elementData找到一个元素为null时,调用fastRemove()方法进行删除元素,并结束遍历,整个remove(Object)方法会返回true,表示找到相等的元素,并删除元素成功
2、如果遍历结束,没有在elementData中找到任何的null元素,remote(Object)方法会返回false,表示删除元素失败
二、传入的元素为非null时
传入的元素为非null,同样从底层数组对象elementData的第一个元素开始遍历,遍历过程中每一轮都会调用传入的元素Object对象的equals()方法与elementData对应下标中的元素对象进行比较,此处使用Object的equals()方法比较元素对象的相等性,equals()方法的返回结果表示相等性,true表示两个对象相等、false表示两个对象不相等
1、当在elementData中找到一个匹配的元素(此时equals()方法返回true),执行fastRemove(int)方法进行删除元素,之后遍历结束,整个remove()方法返回true,表示删除元素成功
2、当遍历结束,没有找到匹配相等的元素,整个remote(Object)方法返回false,表示删除元素失败
fastRemove(int)方法分析
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
}
删除ArrayList对象持有的elementData数组对象中元素的方法,学习一下它的源码
说明:无论删除null元素、还是删除Object元素,都会调用该方法进行删除
1、fail-fast机制,防止多线程下使用ArrayList
将表示修改次数的实例变量modCount加1
2、计算需要向前挪动的元素数量
计算公式为size - index - 1,结果保存在局部变量numMoved中
3、当需要挪动的元素大于0,执行数组元素复制,即数组元素挪动
同样使用System的静态方法arraycopy()完成元素的复制(挪动),删除操作会执行向前复制元素
4、元素总数size减去1
5、将不再使用的下标赋值为null,便于GC未来回收指向的对象
size值作为下标,将其赋值为null
removeAll(Collection)方法分析
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
用于删除指定集合中的多个元素,传入一个Collection对象,表示待删除的元素集合
1、检查传入的Collection必须不能为null
调用了Objects下的方法来判断是否为null
2、调用一个batchRemove()方法完成批量删除工作
调用batchRemove()方法,同时传入一个Collection对象与一个false值
3、返回batchRemove()方法的执行结果给调用方
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.
这里使用arraycopy方法模块将读入的集合数据位置放入写入数据数据集合的位置,放入的为读入的数据
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;
}
用于删除集合中的所有元素
1.获取ArrayList对象持有的底层数组对象elementData
首先获取当前的ArrayList对象持有的底层数组对象elementData,并赋值给同名的elementData持有
2.定义两个局部变量
一个局部变量r表示读取下标(遍历时elementData数组时的,读取下标)
一个局部变量w表示写入的下标
3.定义一个标志位
局部变量modified,初始值为false
4.检查现有的ArrayLIst对象持有的每一个元素是否与传入的Collectin对象的任意一个元素匹配,如果如果不匹配,说明要有保留的元素对象,此时将要保留的元素继续存放在底层数组对象的elementData中下标w处,并且下标w,并且下标w之后会加1,最终w坐标及之前的所有元素均为需要存入的元素
clear()方法分析
将数据清空,直接遍历整个集合并且将null赋值给列表的元素,并且更利于垃圾回收器对集合的回收
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
addAll(Collection<? extends E> c)方法分析
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;
}
首先将传入的Collection类型集合转为数组,然后按照前面的grow的扩容规则进行对集合扩容
然后进行集合复制,将集合放入原集合的后面
addAll(int index, Collection<? extends E> c)方法分析
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;
}
先对集合的范围进行检查
然后将集合转为数组,对集合使用扩容规则进行扩容
rmoveRange(int fromIndex, int toIndex)方法分析
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;
}
先对要移动的位置进行计算,然后对集合进行合并复制,最后循环这个范围区间,对这个区间进行遍历并进行赋空操作
rangeCheck(int index)方法分析
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
对传入的元素下标进行判断比对,如果不在范围区间之内就抛出异常
rangeCheckForAdd(int index)方法分析
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
范围检查是否能添加元素,如果传入下标元素不符合则抛出异常
outOfBoundMsg(int index)方法分析
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
输入下标返回所在位置和列表长度
listIterator(int index)方法介绍
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
java中的ListIterator在Iterator基础上提供了add、set、previous等对列表的操作。但是ListIterator跟Iterator一样,仍是在原列表上进行操作。
Itr方法实现了Iterator方法
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
int cursor; // 下一个要返回的元素的索引()
int lastRet = -1; // 最后一个返回元素的索引;如果没有就是-1
int expectedModCount = modCount;
Itr() {},这里为一个无参构造,是为其子类ListItr在进行构造时能搜索到父类的构造函数
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
判断当前ArrayList的操作值modCount是否等于期望修改值expectedModCount,不相等时会进行异常抛出
public boolean hasNext() {
return cursor != size;
}
这里覆写了Iterator接口中的HasNext()。我们知道这个方法是在询问当前遍历的容器中是否含有下一个元素
cursor != size,这里判断我们的cursor游标是否不等于当前数组的元素个数size。因为如果等于了size就代表指针已经指向了最后一个元素,指针遍历时是从0开始遍历
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
这里的迭代的步骤,这个next()才是真正可以取出的元素的方法,在方法中会先使用hasnext()方法判断是否存在下一个元素,紧接着调用该方法
- 在此方法中首先利用checkForComodification()方法进行对值得修改
- 然后声明一个变量作为指针
- 然后进行判断当发先指针已经大于了数组得长度时,则抛出异常
- 然后在这里将数组进行了复制,建立一个新的Object的方法
- 在这里又进行了判断,判断这个数组是否又被修改过
- 然后对指针进行判断是否可以取出元素给调用者
移除方法
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
remove方法主要是删除利用next()方法返回的元素,这里的cursor是向后的指针,而lastRet相当于向前的指针,ArrayList.this.remove(lastRet)是核心的移除步骤,这是还是使用的是ArrayList内部的remove方法重点是那个索引值——lastRet。记住这个值,这里删除的是当前游标所在位置左移一格的元素(当前一项的前一项)。根据上述next()方法解析来看,当我们元素获取之后,游标cursor右移,同时上一项游标lastRet也进行右移。所以,这个时候lastRet指向的就是我们之前next()方法遍历出的那个元素。这也就解释了为什么我们一般使用这个remove()方法的时候是跟在next()方法之后的
- 第一步,判断latRet是否小于0,如果小于0,则抛出异常,这里当我们拿到了Iterator的时候,这个cursor就已经指向了第一项,也就是说cursor=0,而lastRet是起始位置的左侧,也就是没有(-1),我们不能删除一个不存在的元素
- 第二步,利用方法checkForComodification()方法对修改值modCount进行检查
- 第三步,这里还是围绕操作值
modCount
和预期修改值exceptModCount
展开描述的,使用trycatch来进行异常捕获 - 第四步,极为重要的一步——移除元素。虽说上面已经提到这个方法了,我这里还是提一下。这一步会调用ArrayList内部的remove(int index)方法,注意,这里的remove(int index)方法可是会修改操作值modCount的。
- 第五步,将当前的lastRet赋予当前的cursor。这一步其实就是我们之前在的remove(int index)填坑操作。当我们把当前的lastRet所指向的元素删除时,这里其实就已经有了空缺,但是放心,remove(lastRet)操作通过复制数组已经把这个元素的后一项元素直到最后一项元素往前移动了。所以,我们之前通过next()操作移动的游标cursor其实已经指向了其本身元素的后一项。因此,这里就需要把游标cursor前移一格,也就是往左移,怎么移动呢?不就是lastRet所代表的那个位置吗?也就是说这里会有一步cursor = lastRet的操作让游标cursor指向正确的地方。
- 第六步,将lastRet初始化为最初的-1。其实这一步我并不明白为什么会初始化为-1,我设想的可能是以下两种场景:
情形1:它可以做这种操作:lastRet = lastRet - 1。也就是这个最后一个返回元素的索引向左移动一格。不过,这种初始化-1的操作也不影响程序的正常运行,因为在next()方法中,这个lastRet值依旧会被赋予正确的指向。
情形2:看这个方法的第一步,它会做一个判断。所以,这里如果直接把lastRet变为-1,那么这个逻辑是通畅的。防止连续删除! - 第七步,同步操作值modCount到exceptModCount,这个就不须过多的解释了,因为remove(lastRet)操作会使得当前ArrayList的操作值modCount加1,所以这里为了避免ConcurrentModificationException异常你需要把这个修改了的值更新到我们Itr中的期望修改值exceptModCount内。
ListItr方法继承Itr实现ListIterator
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();
}
}
}
- 第一步首先是listITr的构造方法
ListItr(int index) {
super();
cursor = index;
}
这里的super是上面的Itr的继承,cursor为移动的指针
- 第二部
public boolean hasPrevious() {
return cursor != 0;
}
这里是覆写了ListIterator接口中的hasPrevious方法询问当前遍历容器是否包含上一个元素判断我们的cursor指针是否不等于当前数组内第一个元素的索引值0
- 第三步
public int nextIndex() {
return cursor;
}
返回当前指针的下一个索引
- 第四步
public int previousIndex() {
return cursor - 1;
}
返回当前指针的上一个索引
- 第五步
@SuppressWarnings该批注的作用是给编译器一条指令,告诉它对被批注的代码元素内部的某些警告保持静默。
作用:
告诉编译器忽略指定的警告,不用在编译完成后出现警告信息。
@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];
这个是迭代的重要步骤,方法previous才是真正取出元素的方法,他与next()方法的含义是相反的,next()方法是遍历下一个,而我们previos方法则是遍历上一个,我们经过了知道的hasPrevios()方法判断是否含有上一个元素之后,可以紧接着调用此方法
- 首先会检查是否对modCount进行了修改,然后获取此时指针的位置
- 声明一个局部变量i,并将当前指针的前一位cursor-1赋给他,主要是为向从后往前进行遍历
- 判断指针是否小于当前数组第一个元素的索引0,如果符合条件判断则抛出异常
- 将数组复制获取到一个新的数组
- 这里的判断是判断指针是否大于或等于数组的容量
- 将数组的中内容返回给调用者
这里的lastRet = i
操作,是为了remove()
和set(E e)
这个方法的,当我们想要删除由previous()
方法返回来的这个元素的时候,我的lastRet
必须指向这个元素
第五步
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
这方法是覆写了ListIterator接口中的set(E e)方法,目的是为了修改调用next()方法和previous()方法返回的元素
- 首先会判断我们的
lastRet
是否小于0
。如果小于的话就抛出 - 利用
checkForComodification()
方法对修改值modCount
进行检查,判断当前数组是否发生修改。 - 利用ArrayList类本身的set(int index, E element)方法对我们的元素进行修改。你看,这里传入的第一个索引值就是我们的lastRet,也就是说修改的就是这个lastRet指向的那一项。当然,这里如果出现了异常,则抛出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();
}
}
这里是覆写的ListIterator接口中的add(E e )方法,大致功能为插入,其目的是调用next()方法或previous()方法返回的元素之后插入指定的元素
- 依旧是利用
checkForComodification()
方法对修改值modCount
进行检查,判断当前数组是否发生修改。 - 获取当前的游标
cursor
并赋予新声明的变量i
。 - 这里是极为重要的一步,就是插入动作,你也可以叫添加。它调用了
ArrayList
本类的add(int index, E element)
方法来进行插入操作。第二步中的i
就是插入的位置索引。通俗的说就是,在游标cursor
处进行插入操作。 - 将当前的游标cursor进行加1操作。这步操作的目的是为了不影响游标cursor所代表的含义,即,指向某一个特定元素。你在这个位置新添加了一个元素,那么我当初游标cursor如果还在这个位置的话,是不是就指向了这个新元素?但是其本身是要指向原先你本身cursor指向的那个旧元素,怎么办呢?你就只能进行移位操作,也就是加1了。
- 我们把这个上一项元素指向
lastRet
初始化为-1
。这种操作无非就是不让你进行set(E e)
操作,抑或是不让你进行remove()
操作。为什么呢?因为其实现的方法注释是规定了如此:
subList()方法
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
先判断传入数据是否符合,然后
subListRangeCheck(int fromIndex,int toIndex,int size)
static void subListRangeCheck(int fromIndex, int toIndex, int size) {
if (fromIndex < 0)
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
if (toIndex > size)
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex +
") > toIndex(" + toIndex + ")");
}
此方法为一个静态方法,传入三个参数fromIndex插入起始位置,toIndex传入的数据到的截止位置,传入数据的长度
- 判断三个范围区间是否符合标准
SubList的方法继承了AbstractList实现了RandomAccess
private class SubList extends AbstractList<E> implements RandomAccess {
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size;
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
public E set(int index, E e) {
rangeCheck(index);
checkForComodification();
E oldValue = ArrayList.this.elementData(offset + index);
ArrayList.this.elementData[offset + index] = e;
return oldValue;
}
public E get(int index) {
rangeCheck(index);
checkForComodification();
return ArrayList.this.elementData(offset + index);
}
public int size() {
checkForComodification();
return this.size;
}
public void add(int index, E e) {
rangeCheckForAdd(index);
checkForComodification();
parent.add(parentOffset + index, e);
this.modCount = parent.modCount;
this.size++;
}
public E remove(int index) {
rangeCheck(index);
checkForComodification();
E result = parent.remove(parentOffset + index);
this.modCount = parent.modCount;
this.size--;
return result;
}
protected void removeRange(int fromIndex, int toIndex) {
checkForComodification();
parent.removeRange(parentOffset + fromIndex,
parentOffset + toIndex);
this.modCount = parent.modCount;
this.size -= toIndex - fromIndex;
}
第一步,首先是几个定义的final的常量
父类的引用
- private final AbstractList parent;
父类集合中的位置,如果使用SubList中的subList方法时,则此方法的父类为SubLIst类,不是Arraylist
- private final int parentOffset
子类List在父类ArrayList中的下标位置
- private final int offset;
视图集合的size
- int size;
第二步,进行方法的构造
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
第三步,set和get方法
public E set(int index, E e) {
rangeCheck(index);
checkForComodification();
E oldValue = ArrayList.this.elementData(offset + index);
ArrayList.this.elementData[offset + index] = e;
return oldValue;
}
public E get(int index) {
rangeCheck(index);
checkForComodification();
return ArrayList.this.elementData(offset + index);
}
- 首先是set方法,进行范围检查然后进行数据的查找,通过指针对集合的数据进行赋值、
- get()方法进行范围检查,并且会返回这个数据
- size方法获取集合的长度
- 然后内部大部分为ArrayList的外部方法
forEach方法
@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;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
先判断他是否为空,然年后将其循环输出