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);
        }
    }
}
View Code

 

三  迭代器是失效问题(删除或新增元素)

在创建迭代器之后,除非通过迭代器自身的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);
        }
    }
}
View Code

 

上面异常的本质是

remove操作里涉及到的expectedModCount = modCount;  值为调用容器的iterator()方法返回iterator对象时,容器中的元素个数

在网上查到说这是集合迭代中的一种“快速失败”机制,这种机制提供迭代过程中集合的安全性。

从源代码里可以看到增删操作都会使modCount++,通过和expectedModCount的对比,迭代器可以快速的知道迭代过程中是否存在list.add()类似的操作,存在的话快速失败!

 

而我们也知道,集合元素的删除,不能用foreach  这又是为什么?

foreach中的remove方法实际上使用list.remove一样会报ConcurrentModificationException异常。因为foreach在jvm中还是会解析成Iterator来执行的,实际上和错误例子是一样的效果。

 

那么,我们再来看下为什么用迭代器删除时就可以安全的删除,不会报错呢?

在他的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()

posted on 2020-08-01 14:29  鑫男  阅读(521)  评论(0编辑  收藏  举报

导航