Iterator接口(迭代器)
前言
- 一般遍歷數組都是采用for循環或者增強for,這兩個方法也可以用在集合框架,但是還有一種方法是采用迭代器遍歷集合框架,它是一個對象,實現了Iterator 接口或ListIterator接口。
- 迭代器,使你能夠通過循環來得到或刪除集合的元素。ListIterator 繼承了Iterator,以允許雙向遍歷列表和修改元素。
原理
- 在獲取迭代器的時候,會創建一個集合的副本。同時會創建一個指針指向迭代器迭代集合的其實位置。
方法
- hasNext() :該方法會判斷集合對象是否還有下一個元素,如果已經是最后一個元素則返回false。
- next():把迭代器的指向移到下一個位置,同時,該方法返回下一個元素的引用。
- remove() 從迭代器指向的集合中移除迭代器返回的最后一個元素。
public class Test{
public static void main(String[] args) {
List<String> list=new ArrayList<String>();
list.add("Hello");
list.add("World");
list.add("!");
Iterator<String> ite = list.iterator();
//判斷下一個元素之后是否有值
while(ite.hasNext()){
System.out.println(ite.next());
}
}
}
異常
ConcurrentModificationException異常
異常名:並發修改異常
產生原因:在使用迭代器遍歷集合元素的同時對集合元素進行了操作時拋出此異常
解決辦法:
使用普通for循環遍歷
使用List特有的迭代器遍歷
public class Test {
public static void main(String[] args) {
Collection<String> c = new ArrayList<String>();
c.add("張三");
c.add("李四");
c.add("王五");
Iterator<String> it = c.iterator();
while(it.hasNext()){
//必須是接收it.next(),不然會死循環。
Object obj = it.next();
String s = (String)obj;
if(s.contains("李四")){
// 最后會報ConcurrentModificationException異常
c.add("王五");
}
System.out.println(s);
}
}
}
使用foreach也會出現同樣的異常:
}
for (Object object : c) {
String s = (String)object;
if (s.contains("李四")) {
c.add("王五");
}
System.out.println(s);
}
因為foreach遍歷的本質就是使用iterator迭代器遍歷。
注意:
在迭代的時候是可以刪除元素的。因為會使用iterator中的remove。
看一下ArrayList中iterator重寫的源碼就明白了。
源碼:
public Iterator<E> iterator() {
return new 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;
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
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(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) {
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();
}
}
在ArrayList內部定義了一個內部類Itr,該類實現了Iterator接口。
在Itr中,有三個變量分別是
cursor:表示下一個元素的索引位置
lastRet:表示上一個元素的索引位置
expectModCount:預期被修改的次數
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();
}
在執行remove操作時,會先執行checkForComodification(),判斷集合的修改次數是否合法,然后會執行ArrayList的remove()方法,該方法會將modCount值加1,這里我們將expectedModCount=modCount,使之保持統一。
iterator和for循環的區別:
從數據結構角度分析,for循環適合訪問順序結構,可以根據下標快速獲取指定元素.而Iterator適合訪問鏈式結構,因為迭代器是通過next()來定位的.可以訪問沒有順序的集合.
以上
@Fzxey