Java源码阅读------ArrayList(2)
迭代器
在ArrayList中主要包含了两种迭代器分别是Itr与ListItr,在java8之后又加入了ArrayListSpliterator这个迭代器。这些迭代器分别实现了Iterable,ListIterator,Spliterator接口中的方法。
Itr
Itr类实现了Iterator接口的方法,主要的变量有cursor,lastRet与expectedModCount。
int cursor; // 指向下一个要遍历返回的元素
int lastRet = -1; // 指向上一次遍历返回的元素。如果没有就置为-1
int expectedModCount = modCount;//初始化的时候将在遍历前顺序表已经修改的次数记录
fail-fast
这里简单的讲一下fail-fast机制,上文中我们对顺序表的操作都在维护一个叫modCount的变量,我们每对顺序表修改一次modCount就会对应发生改变,而在Itr初始化的时候,将最近一次modCount的计数给了expectedModCount,并实现了这个方法:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
当在遍历的过程中出现了对顺序表的修改情况,modCount 就会发生改变,这时就会抛出ConcurrentModificationException异常,这就是fail-fast机制最直接的一个解释,这个机制保证了在遍历的过程中不会发生对顺序表的修改。
移除
但是在进行遍历的时候还是有修改的操作的,Itr类给出了一个移除的操作:
public void remove() {
if (lastRet < 0)//移除的是之前返回的元素,如果没有就抛出异常
throw new IllegalStateException();
checkForComodification();//检查fail-fast
try {
ArrayList.this.remove(lastRet);//使用原先的移除方法
cursor = lastRet;//修改索引
lastRet = -1;//之前的元素已经被移除
expectedModCount = modCount;
//最重要的将expectedModCount刷新,否则无法通过fail-fast检查
} catch (IndexOutOfBoundsException ex) {//这个异常来自ArrayList的remove方法中对索引的检查
throw new ConcurrentModificationException();
}
}
遍历
根据cursor的值来判断是否含有下一个
public boolean hasNext() {
return cursor != size;
}
获取下一个:
public E next() {
checkForComodification();//检查fail-fast
int i = cursor;
if (i >= size)//对cursor进行检查
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;//获取数组元素
if (i >= elementData.length)//判断是否超出数组容量
throw new ConcurrentModificationException();
cursor = i + 1;//指向下一个元素
return (E) elementData[lastRet = i];//将lastRet更新并返回相应元素
}
遍历的方法:
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);//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) {//检查遍历边界和fail-fast
consumer.accept((E) elementData[i++]);//循环的进行遍历并将数据传给消费者
}
cursor = i;//刷新索引
lastRet = i - 1;
checkForComodification();//检查fail-fast
}
Consumer接口中的accept方法就是一个没有返回值的函数:
void accept(T t);
同样在ArrayList中也提供了相关的遍历方法:
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);//保证action非空
final int expectedModCount = modCount;//预先保存expectedModCount
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) {//检查fail-fast
throw new ConcurrentModificationException();
}
}
获取
ArrayList提供了获取Itr的方法;
public Iterator<E> iterator() {
return new Itr();
}
ListItr
ListItr继承自Itr,实现了ListIterator接口。
构造函数
ListItr(int index) {
super();
cursor = index;
}
相比与Itr,ListItr提供了对遍历位置的初始设置。
索引与取值
关于索引的操作,ListItr实现了对于表的双向遍历
public boolean hasPrevious() {//边界判断,判断是否是表头
return cursor != 0;
}
public int nextIndex() {//返回下一个元素的索引
return cursor;
}
public int previousIndex() {//返回上一个元素的索引(双向)
return cursor - 1;
}
取值的操作,增加了一个对于上一元素的取值:
public E previous() {
checkForComodification();//检查fail-fast
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];//返回上一元素
}
修改与插入
修改操作,使用的是ArrayList中的set方法,不改变modCount的值。
public void set(E e) {
if (lastRet < 0)//边界判断
throw new IllegalStateException();
checkForComodification();//检查fail-fast
try {
ArrayList.this.set(lastRet, e);//使用ArrayList的set方法来修改
} catch (IndexOutOfBoundsException ex) {//异常来自ArrayList的set中的检查
throw new ConcurrentModificationException();
}
}
插入操作,使用ArrayList中的add方法插入数据。
public void add(E e) {
checkForComodification();//检查fail-fast
try {
int i = cursor;
ArrayList.this.add(i, e);//使用ArrayList中的add方法插入数据
cursor = i + 1;//更新索引
lastRet = -1;
expectedModCount = modCount;//更新expectedModCount
} catch (IndexOutOfBoundsException ex) {//异常来自ArrayList中的add方法对边界的检查
throw new ConcurrentModificationException();
}
}
获取
ArrayList提供了获取ListIterator的方法;
public ListIterator<E> listIterator() {
return new ListItr(0);//默认由起始位置开始遍历
}
ArrayListSpliterator
ArrayListSpliterator实现了Spliterator接口,相比于前两个迭代器,ArrayListSpliterator实现了分割迭代。
构造方法
private final ArrayList<E> list;//内部的引用
private int index; // 当前索引的位置
private int fence; // 边界位置
private int expectedModCount; // 用于检测fail-fast
//构造函数就是将相应的参数设置初值
ArrayListSpliterator(ArrayList<E> list, int origin, int fence, int expectedModCount) {
this.list = list;
this.index = origin;
this.fence = fence;
this.expectedModCount = expectedModCount;
}
边界检测
在进行迭代遍历的时候是以index与fence进行范围约束的。
private int getFence() { //用于获取要进行操作的顺序表的范围
int hi;//用于存储顺序表的截至范围
ArrayList<E> lst;
if ((hi = fence) < 0) {//fence初始时为-1时表示未进行边界检测
if ((lst = list) == null)//之后根据传入的顺序表的大小来确定边界
hi = fence = 0;//表为空时
else {//表不为空expectedModCount取表中的modCount
expectedModCount = lst.modCount;
hi = fence = lst.size;
}
}
return hi;//返回当前表的边界位置
}
超前处理
传入一个消费者的接口,用其来处理当前索引之后一个元素。
public boolean tryAdvance(Consumer<? super E> action) {
if (action == null)//检查消费者接口非空
throw new NullPointerException();
int hi = getFence(), i = index;//设置边界
if (i < hi) {//越界检查
index = i + 1;//指向下一个元素
E e = (E)list.elementData[i];//取元素
action.accept(e);//传给消费者使用
if (list.modCount != expectedModCount)//检测fail-fast
throw new ConcurrentModificationException();
return true;
}
return false;//越界则返回false
}
分割处理
ArrayListSpliterator提供了划分的方法可以将相应范围的ArrayListSpliterator等分返回第位的新的ArrayListSpliterator。
public ArrayListSpliterator<E> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid) ? null : new ArrayListSpliterator<E>(list, lo, index = mid, expectedModCount);
}
遍历
ArrayListSpliterator与其他的迭代器一样提供了遍历的接口。
public void forEachRemaining(Consumer<? super E> action) {
int i, hi, mc;
ArrayList<E> lst; Object[] a;
if (action == null)//检查消费接口非空
throw new NullPointerException();
if ((lst = list) != null && (a = lst.elementData) != null) {//迭代数据的非空判断
if ((hi = fence) < 0) {//设置遍历边界与expectedModCount
mc = lst.modCount;
hi = lst.size;
}
else
mc = expectedModCount;
if ((i = index) >= 0 && (index = hi) <= a.length) {//进行边界的判断
for (; i < hi; ++i) {//进行遍历操作将数据送到消费者的accept方法
E e = (E) a[i];
action.accept(e);
}
if (lst.modCount == mc)//检查fail-fast
return;
}
}
throw new ConcurrentModificationException();//如果出现fail-fast抛出异常
}
其余函数
除此之外,还有两函数estimateSize表示还有多少的元素需要遍历,characteristics用于返回Spliterator的特征为ORDERED,SIZED与SUBSIZED。
public long estimateSize() {
return (long) (getFence() - index);
}
public int characteristics() {
return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
}
/*Spliterator接口中有以下定义
public static final int ORDERED = 0x00000010;//元素是有序的(每一次遍历结果相同)
public static final int DISTINCT = 0x00000001;//元素不重复
public static final int SORTED = 0x00000004;//元素是按一定规律进行排列(指定比较器)
public static final int SIZED = 0x00000040;//元素数量确定
public static final int NONNULL = 0x00000100;//表示迭代器中没有null元素
public static final int IMMUTABLE = 0x00000400;//表示迭代器中元素不可变
public static final int CONCURRENT = 0x00001000;//表示迭代器可以多线程操作
public static final int SUBSIZED = 0x00004000;//表示子Spliterators都具有SIZED特性
*/
获取
ArrayList提供了获取Spliterator的方法;
public Spliterator<E> spliterator() {
return new ArrayListSpliterator<>(this, 0, -1, 0);
}