Iterator是什么?
public interface Iterator<E> {...}
iterator被称之为顺序遍历迭代器,jdk中默认对集合框架中数据结构做了实现。iterator在实际应用中有一个比较好的点就是,可以一边遍历一遍删除元素(不过需要自实现,好在jdk都帮你做了,哈哈),后面拿ArrayList.iterator()来说明怎么做到这点的。
Iterator结构
//是否能遍历到下一个元素。是返回true,否则返回false
boolean hasNext();
//指向下一个元素
E next();
//删除当前元素,需要实现者重写,默认会抛错
default void remove() {
throw new UnsupportedOperationException("remove");
}
//顺序遍历元素,对每个元素执行指定事件,一直到遍历完所有元素
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
ArrayList.iterator()解析
public Iterator<E> iterator() {
//返回一个顺序遍历迭代器
return new Itr();
}
private class Itr implements Iterator<E> {
//指向下次将遍历到的位置
int cursor;
//当前遍历到的位置,当为-1时,表示未指向元素
int lastRet = -1;
//结构变更次数
int expectedModCount = modCount;
//是否返拥有下个元素
public boolean hasNext() {
return cursor != size;
}
//遍历到下个元素
@SuppressWarnings("unchecked")
public E next() {
//ArrayList是否发生结构变更操作
checkForComodification();
//得到当前遍历的位置
int i = cursor;
//是否超出size大小
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
//并发修改异常,elementData的结构发生变更
//(疑惑:这一段代码的意义,可以和forEachRemaining对比一下)
if (i >= elementData.length)
throw new ConcurrentModificationException();
//指向下次遍历到的位置
cursor = i + 1;
//返回当前位置的值
return (E) elementData[lastRet = i];
}
public void remove() {
//如果当前遍历器未指向任何元素时,则报错
//未执行next()时和执行remove方法时 lastRet=-1
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
//调用arrayList.remove方法来删除元素
ArrayList.this.remove(lastRet);
//指针指向当前删除位置
cursor = lastRet;
//lastRet置为-1,表示当前遍历器未指向任何元素
lastRet = -1;
//这里是iteraotr可以遍历到元素后可以删除元素的关键
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
//consumer不能为空校验
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
//遍历器还有元素未遍历完
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
//并发修改异常,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();
}
}
看到这一段代码的时候,我对forEachRemaining方法里面检查ConcurrentModificationException并发操作异常的情况有疑惑,在校验元素的size后,再将执行 Object[] elementData = ArrayList.this.elementData;再对elementData进行遍历操作同时并会对expectedModCount进行判断,因此遍历之前检查(i >= elementData.length) 感觉并没有什么用。
另外,ArrayList自身就不是线程安全的,modCount也只是用来保证快速失败,因此不要在多线程的环境下使用iterator进行并行遍历操作。如果需要并行遍历可以使用Spliterator进行并行遍历。
ArrayList.iterator本质上还是操作原list对象,只是通过一个单向指向list的索引而对原对象进行引用,因此iterator在工作的时候,我们如果对远list对象进行了结构上的变更,此时会通过expectedModCount比较modCount是否相等来实现fail-fast。而ArrayList.iterator.remove方法没有问题,是因为操作过后会同步更新expectedModCount的值。