java 迭代器
一 概念
迭代器是一个对象,它的工作是遍历并选择序列中的对象,它提供了一种访问一个容器对象中的各个元素的方法,而不必暴露容器对象的内部细节。
作用:
1 通过迭代器,开发人员不需要了解容器结构,就可以遍历容器元素。被称为轻量级容器(创建迭代器代价很小)
2 它的特点是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出 ConcurrentModificationException
异常
二 用法
Java中的Iterator功能比较简单,并且只能单向移动:
(1) 使用方法iterator()要求容器返回一个Iterator对象。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新返回的元素删除。
简单例子
import java.util.*; public class Muster { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("a"); list.add("b"); list.add("c"); Iterator it = list.iterator(); while(it.hasNext()){ String str = (String) it.next(); System.out.println(str); } } }
三 迭代器是失效问题(删除或新增元素)
在创建迭代器之后,除非通过迭代器自身的remove 或add 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出ConcurrentModificationException
。
public class ArrayListTest { public static void main(String args[]) { List<String> strList = new ArrayList<String>(); //迭代器 Iterator<String> iterator = strList.iterator(); //修改了集合 for (int i = 0; i < 10; i++) { strList.add("string" + i); } while (iterator.hasNext()) { System.out.println(iterator.next()); } }
运行该段代码,会发现其抛出如下异常:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at com.xyh.collection.ArrayListTest.main(ArrayListTest.java:21)
原因在于在迭代器创建之后,通过ArrayList自身的add方法对列表进行了修改,导致迭代器失效。当将蓝色创建迭代器的代码移动到while循环的上方后,则不会出现该问题。即创建迭代器后不能再通过容器的add/remove方法来改变容器的数据,否则会导致迭代器的失效。
包括下面这种写法,也是会导致同样的异常
public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); list.add("e"); list.add("f"); list.add("g"); list.add("h"); Iterator<String> it = list.iterator(); while (it.hasNext()) { String str = it.next(); if (str.equals("f")) { list.remove(str); } } }
上面异常的本质是
remove操作里涉及到的expectedModCount = modCount; 值为调用容器的iterator()方法返回iterator对象时,容器中的元素个数
在网上查到说这是集合迭代中的一种“快速失败”机制,这种机制提供迭代过程中集合的安全性。
而我们也知道,集合元素的删除,不能用foreach 这又是为什么?
那么,我们再来看下为什么用迭代器删除时就可以安全的删除,不会报错呢?
在他的remove函数中可以看到下面的一句话,首先其实还是调用了ArrayList的remove函数
ArrayList.this.remove(lastRet)
但是在调用完该函数后,他又进行了如下操作
expectedModCount = modCount;
相当于将最新的版本号告诉了迭代器,所以迭代器在进行异常检查的时候就不会报错,因为他俩是相等的
四 迭代器删除元素
public class ArrayListTest { public static void main(String args[]) throws Exception { List<String> strList = new ArrayList<String>(); for (int i = 0; i < 10; i++) { strList.add("string" + i); } Iterator<String> iterator = strList.iterator(); while (iterator.hasNext()) { //iterator.next() 游标指向了下一个元素 if (iterator.next().equals("string3")) { iterator.remove(); //iterator.remove()移除的是最近一次iterator.next()所获取的对象 } } iterator = strList.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } }
上述代码中,iterator.remove操作移除的对象时string3。如果将蓝色while循环替换为如下的代码:
int index = 0; while (iterator.hasNext()) { if (++index == 3) { iterator.remove(); } System.out.println(iterator.next());
本代码的初衷是希望通过使用迭代器来删除第三个元素即string2,并将未删除的元素依次打印出来,殊不知删除的却是string1元素,即:iterator.remove()操作删除的是上一次next元素获取的对象。因此在这里可以发现,如果要通过迭代器删除一个元素,首先要通过next方法获取该元素。
需要删除元素,也可以把迭代器遍历过程中,把需要删除的元素放入新集合,遍历完成后一次性删除 removeAll()