Iterator迭代器

水深无声 2022-06-10 01:43 534阅读 0赞

Iterator是一个迭代器接口,它专门用于迭代各种Collection集合,包括Set集合和List集合。如果查阅JDK的API文档将发现,Iterator迭代器接口只有一个Scanner实现类。显然Scanner并不能用于迭代Set、List集合,那迭代List、Set集合的Iterator迭代器实现类在哪里

下面测试使用Iterator迭代各种集合所返回的Iterator对象。

  1. enum Color {RED,
  2. YELLOW;
  3. }
  4. public class T {
  5. /** * @param args */
  6. public static void main(String[] args) {
  7. HashSet<String> hashSet = new HashSet<String>();
  8. System.out.println("HashSet的Iterator:" + hashSet.iterator());
  9. LinkedHashSet<String> linkedHashSet = new LinkedHashSet<String>();
  10. System.out.println("LinkedHashSet的Iterator:" +
  11. linkedHashSet.iterator());
  12. TreeSet<String> treeSet = new TreeSet<String>();
  13. System.out.println("TreeSet的Iterator:" + treeSet.iterator());
  14. EnumSet<Color> enumSet = EnumSet.allOf(Color.class);
  15. System.out.println("EnumSet的Iterator:" + enumSet.iterator());
  16. ArrayList<String> arrayList = new ArrayList<String>();
  17. System.out.println("ArrayList的Iterator:" + arrayList.iterator());
  18. Vector<String> vector = new Vector<String>();
  19. System.out.println("Vector的Iterator:" + vector.iterator());
  20. LinkedList<String> linkedList = new LinkedList<String>();
  21. System.out.println("LinkedList的Iterator:" + linkedList.iterator());
  22. ArrayDeque<String> arrayDeque = new ArrayDeque<String>();
  23. System.out.println("ArrayDeque的Iterator:" + arrayDeque.iterator());
  24. }
  25. }

上面程序创建了Java的各种集合,然后调用这些集合的iterator()方法来获取各种集合对应的Iterator对象。运行上面程序,结果如下

  1. HashSetIteratorjava.util.HashMap$KeyIterator@1428ea
  2. LinkedHashSetIteratorjava.util.LinkedHashMap$KeyIterator@18a49e0
  3. TreeSetIteratorjava.util.TreeMap$KeyIterator@3c9217
  4. EnumSetIteratorjava.util.RegularEnumSet$EnumSetIterator@127fa12
  5. ArrayListIteratorjava.util.ArrayList$Itr@192c8d9
  6. VectorIteratorjava.util.Vector$Itr@1c05ffd
  7. LinkedListIteratorjava.util.LinkedList$ListItr@de1b8a
  8. ArrayDequeIteratorjava.util.ArrayDeque$DeqIterator@1c0f2e5

从上面运行结果来看,除了EnumSet集合的Iterator就是RegularEnumSet的一个内部类之外,所有Set集合对应的Iterator都是它对应的Map类的内部类KeyIterator。这是因为,Set集合底层是通过Map来实现的

ArrayList和Vector的实现基本相同,除了ArrayList是线程不安全的,而Vector是线程安全的。因此,它们两个对应的Iterator都是相同的,即AbstractList的内部类Itr。LinkedList集合对应的Iterator是其内部类ListItr。ArrayDeque集合对应的Iterator是ArrayDeque$DeqIterator。

通过上面介绍不难发现,对于Iterator迭代器而言,它只是一个接口。Java要求各种集合都提供一个iterator()方法,该方法可以返回一个Iterator用于遍历该集合中的元素,至于返回的Iterator到底是哪种实现类,程序并不关心,这就是典型的“迭代器模式”。

Java的Iterator和Enumeration两个接口都是迭代器模式的代表之作,它们就是迭代器模式里的“迭代器接口”,所谓迭代器模式指得是,系统为遍历多种数据列表、集合、容器提供一个标准的“迭代器接口”,这些数据列表、集合、容器就可面向相同的“迭代器接口”编程,通过相同的迭代器接口访问不同数据列表、集合、容器里的数据。不同的数据列表、集合、容器如何实现这个“迭代器接口”,则交给各数据列表、集合、容器自己完成。

迭代时删除指定元素

由于Iterator迭代器只负责对各种集合所包含的元素进行迭代,它自己并没有保留集合元素,因此使用Iterator进行迭代时,通常不应该删除集合元素,否则将引发ConcurrentModificationException异常。当然,Java允许通过Iterator提供的remove()方法删除刚刚迭代的集合

但实际上在某些特殊情况下,可以在使用Iterator迭代集合时直接删除集合中某个元素。示例如下

  1. public class Demo {
  2. /** * @itmyhome */
  3. public static void main(String[] args) {
  4. // TODO Auto-generated method stub
  5. ArrayList<String> list = new ArrayList<String>();
  6. list.add("A");
  7. list.add("B");
  8. list.add("C");
  9. list.add("D");
  10. list.add("E");
  11. for (Iterator<String> ite = list.iterator(); ite.hasNext();) {
  12. String str = ite.next();
  13. if ("D".equals(str)) { //①
  14. // 删除集合中倒数第二个元素
  15. list.remove(str);
  16. }
  17. }
  18. }
  19. }

上面程序中尝试使用Iterator遍历ArrayList集合时,直接调用List的remove()方法删除指定集合元素。运行上面程序,发现该程序完全可以正常结束,并未发生任何异常。

实际上,对于ArrayList、Vector、LinkedList等List集合而言,当使用Iterator遍历它们时,如果正在遍历倒数第2个集合元素,使用List集合的remove()方法删除集合的任意一个元素并不会引发ConcurrentModificationException异常,当正在遍历其他元素时删除其他元素就会引发该异常。也就是说,如果将程序中①行代码改为等于其他元素,就会引发ConcurrentModificationException异常。

为何使用Iterator遍历List集合的倒数第2个元素时,直接使用List集合的remove()方法删除List集合的倒数第2个元素没有引发ConcurrentModificationException异常呢?关键在于List集合对应的Iterator实现类(Itr)的hasNext()方法,下面是该方法的实现。

  1. public boolean hasNext() {
  2. // 如果下一步即将访问的集合元素的索引不等于集合的大小,就返回true
  3. return cursor != size;
  4. }

对于Itr遍历器而言,它判断释放还有下一个元素的标准很简单:如果下一步即将访问的元素的索引不等于集合的大小,就会返回true,否则就会返回false。当程序使用Iterator遍历List集合的倒数第2个元素时,下一步即将访问的元素的索引为size()-1。如果此时通过List删除集合的任意一个元素,将导致集合size()变为size()-1,折将导致hasNext()方法返回false。也就是说,遍历将提前结束,Iterator不会访问List集合的最后一个元素。

可以在程序①行代码之前添加如下代码来输出每个被遍历到的集合元素

  1. System.out.println(str);

添加上面代码之后,可以看到永远不会访问到ArrayList的最后一个集合元素–这就是Itr类的hasNext()方法导致的。

也就是说,如果使用Itr正在遍历List集合的倒数第2个元素,程序直接调用List集合的remove()方法删除任意元素后,程序不会调用Itr的next()方法访问集合的下一个元素。否则Itr总是会引发ConcurrentModificationException异常。Itr的next()方法中调用checkForComodification()方法来检查集合是否被修改,代码如下:

  1. final void checkForComodification() {
  2. // 如果集合的修改次数和遍历之前的修改次数不相等
  3. if (modCount != expectedModCount)
  4. throw new ConcurrentModificationException();
  5. }

Itr的checkForComodification()实现非常简单:遍历之前使用expectedModCount保留该集合被修改的次数,每次获取集合的下一个元素之前检查集合的当前修改次数(modCount)与遍历之前的修改次数(expectedModCount)是否相等,如果不相等就直接抛出ConcurrentModificationException异常

发表评论

表情:
评论列表 (有 0 条评论,534人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Iterator

    一、迭代器是什么 迭代器很重要,是遍历线性数据结构(链表)的重要方法之一。 迭代器取出元素的原理: 判断集合中还有没有元素,有就取出来 再判断集合中还有没有元素,有

    相关 Iterator

    迭代器是一种设计模式,它用于遍历集合或容器中的元素,能够访问集合的元素而无需关心集合的内部结构: 特点: 1. 封装集合访问:迭代器封装了对集合元素的访问,通过迭代器访问

    相关 Iterator

    Iterator是一个迭代器接口,它专门用于迭代各种Collection集合,包括Set集合和List集合。如果查阅JDK的API文档将发现,Iterator迭代器接口只有一个

    相关 arrayList,Iterator

    ArrayList: 集合的体系结构: 由于不同的数据结构(数据的组织,存储的方式),所以java为我们提供了不同的结合。 但是不用的结合他们的功能都是相似的,不断地向上