Java中使用Iterator(迭代器)来循环遍历一个集合中的元素。一个特定的迭代器是一个实现了Iterator或者ListIterator接口的对象。Iterator可以遍历一个集合,获取或者移除其中的元素;ListIterator扩展了Iterator,可以双向遍历列表或者修改元素,但是只有实现了List接口的集合类才能使用。
每个集合类都提供了iterator()方法来返回集合的迭代器。使用一个迭代器可以分为以下几部:
下面给出一个简单的例子来说明使用方法:
import java.util.*;
public class IteratorDemo {
public static void main(String args[]) {
// Create an array list
ArrayList al = new ArrayList();
// add elements to the array list
al.add("C");
al.add("A");
al.add("E");
al.add("B");
al.add("D");
al.add("F");
// Use iterator to display contents of al
System.out.print("Original contents of al: ");
Iterator itr = al.iterator();//调用集合类的方法定义了一个迭代器对象
while(itr.hasNext()) {
Object element = itr.next();
System.out.print(element + " ");
}
System.out.println();
// Modify objects being iterated
ListIterator litr = al.listIterator();
while(litr.hasNext()) {
Object element = litr.next();
litr.set(element + "+");//使用ListIterator的set()修改集合中的内容
}
System.out.print("Modified contents of al: ");
itr = al.iterator();
while(itr.hasNext()) {
Object element = itr.next();
System.out.print(element + " ");
}
System.out.println();
// Now, display the list backwards
System.out.print("Modified list backwards: ");
while(litr.hasPrevious()) {
Object element = litr.previous();//反向遍历
System.out.print(element + " ");
}
System.out.println();
}
}
运行结果如下:
Original contents of al: C A E B D F
Modified contents of al: C+ A+ E+ B+ D+ F+
Modified list backwards: F+ D+ B+ E+ A+ C+
通过上述例子可以看出迭代器对象就像是一个集合类对象的游标指针,通过hasNext()判断是否还有下一个元素,通过next()移动游标指向下一个元素。
下面我们通过一个具体的集合类AbstractList对Iterator接口的实现来了解一下它的背后机制。
public abstract class AbstractList extends AbstractCollection implements List {
// List接口实现了Collection, Iterable
protected AbstractList() {...}
public Iterator iterator() {
return new Itr();//这里返回一个迭代器
}
private class Itr implements Iterator { //内部类Itr实现迭代器
int cursor = 0;
int lastRet = -1;
int expectedModCount = modCount; //在进行迭代的过程中不能修改集合类中元素的数量
public boolean hasNext() { //hasNext()方法
return cursor != size(); //调用类本身的size()方法获取元素的数量
}
public E next() { //next()方法
checkForComodification();
try {
int i = cursor;
E next = get(i); //调用类本身的get()方法
lastRet = i; //上次返回(遍历)
cursor = i + 1; //将要遍历到的元素
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0) //只有先获取next()才能移除
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1; //又回到没有获取的状态所以置为-1
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
这里需要注意的是checkForComodification方法,这是集合迭代中的一种【快速失败Fail-Fast】机制,提供迭代过程中集合的安全性。modCount记录这该集合结构被修改的次数,因为ArrayList是线程不安全的,如果有其他线程在使用迭代器中修改了集合结构,就会抛出ConcurrentModificationException()异常。
比如说在遍历集合是进行插入操作:
while(itr.hasNext()) {
Object element = itr.next();
System.out.print(element + " ");
al.add("H");
}
就会出现错误:
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)//可以看出是在Itr类的next()方法中调用了checkForComodification()方法导致的异常
at file.main(file.java:21)
还比如说在第一次使用next()之前就使用remove()也会发生异常:
Iterator itr = al.iterator();//调用集合类的方法定义了一个迭代器对象
itr.remove();
while(itr.hasNext()) {
Object element = itr.next();
System.out.print(element + " ");
// al.add("H");
}
错误如下:
Exception in thread "main" java.lang.IllegalStateException
at java.util.ArrayList$Itr.remove(ArrayList.java:844)
at file.main(file.java:19)
下面再来谈谈foreach的用法。
感觉在实际应用中还是会更多使用foreach来遍历一个集合,比起Interator更方便,和一般的for循环相比不用关心下标的起始值和终止值。其为JDK5.0新增的一个循环结构,用于处理集合中的每个元素而不用考虑下标。使用时在for循环中定义一个临时变量用于暂存集合中的每个元素,如下所示。
import java.util.*;
public class TestIterator {
public static void main(String[] args) {
List list=new ArrayList ();
for(int i=0;i<10;i++){
list.add(new String("list"+i) );
}
for(String str:list){
System.out.println(str);
}
}
}
在比较foreach和Iterator的效率问题时,
如果查看编译后的字节码,会发现foreach最终被编译器转为对iterator.next()的调用。
因此写代码使用foreach语法是首选,代码更优美,也不容易出错。
参考:
1.https://www.tutorialspoint.com/java/java_using_iterator.htm
2.http://www.cnblogs.com/hasse/p/5024193.html
3.http://blog.csdn.net/janekeyzheng/article/details/41679497