Java迭代器
迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。迭代器修改了常规指针的接口,所谓迭代器是一种概念上的抽象:那些行为上像迭代器的东西都可以叫做迭代器。然而迭代器有很多不同的能力,它可以把抽象容器和通用算法有机的统一起来。
迭代器作为一种设计模式,它提供了一种方法顺序访问一个聚合对象中的各个元素,而又无需暴露该对象的内部实现,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。
Java中的Iterator一般称为“轻量级”对象,创建它的代价是比较小的。这里不会去考究迭代器这种设计模式,仅在JDK代码层面上谈谈迭代器的时候以及使用迭代器的好处。
package java.util;
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
Iterator是作为一个接口存在的,它定义了迭代器所具有的功能。对于这三个方法所实现的功能,字面意义就是了。hasNext()
方法可以判断对象中是否还存在元素,next()
方法可以获取当前元素并移动到下一个元素位置,remove()
方法可以删除当前元素。
我们以ArrayList类为例,看看这三个方法如何实现。
/**
* Returns an iterator over the elements in this list in proper sequence.
*
* <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
*
* @return an iterator over the elements in this list in proper sequence
*/
public Iterator<E> iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
*/
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();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
对于上述的代码不难看懂,有点疑惑的是int expectedModCount = modCount;
这句代码,其实这是集合迭代中的一种“快速失败”机制,这种机制提供迭代过程中集合的安全性。阅读源码就可以知道ArrayList中存在modCount对象,增删操作都会使modCount++,通过两者的对比迭代器可以快速的知道迭代过程中是否存在list.add()类似的操作,存在的话快速失败!
以一个实际的例子来看:
Iterator<Character> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
list.add('o');
}
运行结果为:
仔细观察上述的各个方法,我们在源码中就会发现一个特别的属性modCount,API解释如下:
The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.
它的主要作用是:expectedModCount保存的是遍历开始前的modCount,一旦开始遍历expectedModCount值不会发生改变,但是如果在遍历的时候又别的线程调用集合的add()或者remove方法就会引起modCount++,从而触发快速失败机制。
而对于快速失败机制而言,它的作用是: 当有线程遍历集合时,如果有线程想对集合进行添加或者删除元素的操作时,遍历集合的线程立马抛出异常,从而遍历集合的线程结束。即不允许遍历方法和添加删除方法同时执行。
迭代器的好处
- 1、迭代器可以提供统一的迭代方式。
- 2、迭代器也可以在对客户端透明的情况下,提供各种不同的迭代方式。
- 3、迭代器提供一种快速失败机制,防止多线程下迭代的不安全操作。
不过对于第三点尚需注意的是:就像上述事例代码一样,我们不能保证迭代过程中出现“快速失败”的都是因为同步造成的,因此为了保证迭代操作的正确性而去依赖此类异常是错误的!