什么是迭代器:
维基百科解释:迭代器(iterator)有时又称光标(cursor)是程序设计的软件设计模式,可在容器对象(container,例如链表或数组)上遍访的接口,设计人员无需关心容器对象的内存分配的实现细节。
我们解释一下上面这段话:
讲到迭代器,就离不开容器,也是是Java中经常讲到的集合(Java集合框架的集合类,我们有时候称之为容器)。容器的种类有很多种,比如ArrayList、LinkedList、HashSet…,(这里我们先不考虑Map接口下面的类)每种容器都有自己的特点,ArrayList底层维护的是一个数组;LinkedList是链表结构的;HashSet依赖的是哈希表,每种容器都有自己特有的数据结构。
正是因为容器的内部结构不同,所以很多时候可能不知道该怎样去遍历一个容器中的元素。所以为了使对容器内元素的操作更为简单,Java引入了迭代器模式,把迭代抽取为一个接口,更为简洁高效的实现容器的遍历!
把访问逻辑从不同类型的集合类中抽取出来,从而避免向外部暴露集合的内部结构,达到设计人员无需关心容器对象的内存分配的实现细节的目的。
查看Iterator接口源码,发现Iterator主要有三个方法:hasNext()、next()、remove()
我们以一个具体的类来看迭代器的原理和实现过程(以ArrayList为例)
1、hasNext()方法
可以看到,hasNext方法官方说明是判断是否存在下一个元素,这里通过cursor是否与集合中的元素个数相同
来判断集合中元素是否遍历完毕,如果没有,就返回true。
2、next()方法
注意:这里新创建的迭代器默认指向列表中的第一个元素!!!
这里调用next()方法,是返回当前元素, 指针(cursor)并指向下一个元素。这里跟我们前面那个Iterator那里的说法我觉得是有点冲突的—-Returns the next element in the iteration.
(其实我这里也没品出来,不过我们清楚源码怎么实现就行了,网上各有各的说法,有的说返回当前,有的说返回下一个,与其说这么多,不如看看源码怎么实现)
3、remove()方法
remove()删除next方法返回的元素,一般和next方法一起用。这里注意如果调用remove之前没有调用next是不合法的,会抛出IllegalStateException。迭代器的remove操作删除的是最近一次由next操作获取的元素,而不是当前游标所指向的元素。
这里插着讲一下,Iterator遍历时不可以删除集合中的元素问题
在使用Iterator的时候禁止对所遍历的集合进行改变其大小结构的操作。例如: 在使用Iterator进行迭代时,如果对集合进行了add、remove操作就会出现ConcurrentModificationException
异常,除非通过迭代器自身的remove或add方法(比如ListIterator中的add方法)从结构上对列表进行修改。
原因是因为在你迭代之前,迭代器已经被创建出来了(比如Iterator it = list.iterator();),如果在迭代的过程中,又对list进行了改变其集合大小的操作,那么Java就会给出异常。因为此时Iterator对象已经无法主动同步list做出的改变,Java会认为你做出这样的操作是线程不安全的,就会给出善意的提醒(抛出ConcurrentModificationException异常)
实现过程就是通过checkForComodification()方法,具体实现可以详细看源码!
这里附上一位博主的详细解释:深入理解Java中的迭代器
文章末尾附上ArrayList中迭代器一段详细源码
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;
// prevent creating a synthetic constructor
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
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int size = ArrayList.this.size;
int i = cursor;
if (i < size) {
final Object[] es = elementData;
if (i >= es.length)
throw new ConcurrentModificationException();
for (; i < size && modCount == expectedModCount; i++)
action.accept(elementAt(es, i));
// update once at end to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}