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);
}
发布了62 篇原创文章 · 获赞 36 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/sinat_36945592/article/details/97542613