资讯 小学 初中 高中 语言 会计职称 学历提升 法考 计算机考试 医护考试 建工考试 教育百科
栏目分类:
子分类:
返回
空麓网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
空麓网 > 计算机考试 > 软件开发 > 后端开发 > Java

Java--Iterator迭代器(集合的遍历)

Java 更新时间: 发布时间: 计算机考试归档 最新发布

Java--Iterator迭代器(集合的遍历)

目录

一.Iterator接口API

二.迭代器的实现原理

三.ListIterator

四.ConcurrentModificationException(使用迭代器报这个错可以来看下原因及解决方案)

        🔔modCount与fail-fast机制


一.Iterator接口API

        👑在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口java.util.Iterator。Iterator接口也是Java集合中的一员,但它与Collection、Map接口有所不同,Collection接口与Map接口主要用于存储元素,而Iterator主要用于迭代访问(即遍历)Collection中的元素,因此Iterator对象也被称为迭代器。

        👉想要遍历Collection集合,那么就要获取该集合迭代器完成迭代操作,下面介绍一下获取迭代器的方法:

  • public Iterator iterator(): 获取集合对应的迭代器,用来遍历集合中的元素的。

下面介绍一下迭代的概念:

  • 迭代:即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。

Iterator接口的常用方法如下:

  • public E next():返回迭代的下一个元素。

  • public boolean hasNext():如果仍有元素可以迭代,则返回 true。

  • public void remove():通过迭代器删除元素

        🥳接下来我们通过案例学习如何使用Iterator迭代集合中元素:

public class IteratorDemo {  	public static void main(String[] args) {        // 使用多态方式 创建对象        Collection coll = new ArrayList();        // 添加元素到集合        coll.add("串串星人");        coll.add("吐槽星人");        coll.add("汪星人");        //遍历        //使用迭代器 遍历   每个集合对象都有自己的迭代器        Iterator it = coll.iterator();        //  泛型指的是 迭代出 元素的数据类型        while(it.hasNext()){ //判断是否有迭代元素            String s = it.next();//获取迭代出的元素            System.out.println(s);        }  	}}

 tips::在进行集合元素取出时,如果集合中已经没有元素了,还继续使用迭代器的next方法,将会发生java.util.NoSuchElementException没有集合元素的错误。


二.迭代器的实现原理

        🚀我们在之前案例已经完成了Iterator遍历集合的整个过程。当遍历集合时,首先通过调用集合的iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。

         🚀Iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素,为了让初学者能更好地理解迭代器的工作原理,接下来通过一个图例来演示Iterator对象迭代元素的过程:

        🎁总结:在调用Iterator的next方法之前,迭代器的索引位于第一个元素之前,指向第一个元素,当第一次调用迭代器的next方法时,返回第一个元素,然后迭代器的索引会向后移动一位,指向第二个元素,当再次调用next方法时,返回第二个元素,然后迭代器的索引会再向后移动一位,指向第三个元素,依此类推,直到hasNext方法返回false,表示到达了集合的末尾,终止对元素的遍历。  


三.ListIterator

        ⭕️List 集合额外提供了一个 listIterator() 方法,该方法返回一个 ListIterator 对象, ListIterator 接口继承了 Iterator 接口,提供了专门操作 List 的方法:

  • void add():通过迭代器添加元素到对应集合

  • void set(Object obj):通过迭代器替换正迭代的元素

  • void remove():通过迭代器删除刚迭代的元素

  • boolean hasPrevious():如果以逆向遍历列表,往前是否还有元素。

  • Object previous():返回列表中的前一个元素。

  • int previousIndex():返回列表中的前一个元素的索引

  • boolean hasNext()

  • Object next()

  • int nextIndex()

        💎下面代码示例 , 这里用法和Iterator一致 , 不做过多的赘述 ,需要注意ListIterator仅针对List集合才可以使用 

	public static void main(String[] args) {		List c = new ArrayList<>();		c.add(new Student(1,"张三"));		c.add(new Student(2,"李四"));		c.add(new Student(3,"王五"));		c.add(new Student(4,"赵六"));		c.add(new Student(5,"钱七"));				//从指定位置往前遍历		ListIterator listIterator = c.listIterator(c.size());		while(listIterator.hasPrevious()){			Student previous = listIterator.previous();			System.out.println(previous);		}	}

四.ConcurrentModificationException(使用迭代器报这个错可以来看下原因及解决方案)

        🔔modCount与fail-fast机制

           ⭕️当使用foreach或Iterator迭代器遍历集合时,同时调用迭代器自身以外的方法修改了集合的结构,例如调用集合的add和remove方法时,就会报ConcurrentModificationException。

public class TestForeach {    public static void main(String[] args) {        Collection list = new ArrayList<>();        list.add("hello");        list.add("java");        list.add("atguigu");        list.add("world");                Iterator iterator = list.iterator();        while(iterator.hasNext()){            list.remove(iterator.next());//指的就是这里,遍历时,使用了集合的remove方法            //iterator.remove(); 使用迭代器的删除方法,删除元素问题就解决了        }    }} 

        ✍️报错的原因想必到现在你已经知道为什么了 , 下面的内容会展开说下modCount与fail-fast机制 , 感兴趣的话可以看下

        🔺如果在Iterator、ListIterator迭代器创建后的任意时间从结构上修改了集合(通过迭代器自身的 remove 或 add 方法之外的任何其他方式),则迭代器将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就完全失败,而不是冒着在将来不确定的时间任意发生不确定行为的风险。

        🔺这样设计是因为,迭代器代表集合中某个元素的位置,内部会存储某些能够代表该位置的信息。当集合发生改变时,该信息的含义可能会发生变化,这时操作迭代器就可能会造成不可预料的事情。因此,果断抛异常阻止,是最好的方法。这就是Iterator迭代器的快速失败(fail-fast)机制。

        🔺注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测 bug。例如:

	@Test	public void test02() {		ArrayList list = new ArrayList<>();		list.add("hello");		list.add("java");		list.add("atguigu");		list.add("world");		        //以下代码没有发生ConcurrentModificationException异常		Iterator iterator = list.iterator();		while(iterator.hasNext()){			String str = iterator.next();						if("atguigu".equals(str)){				list.remove(str);			}		}	}

那么如何实现快速失败(fail-fast)机制的呢?

  • 在ArrayList等集合类中都有一个modCount变量。它用来记录集合的结构被修改的次数。

  • 当我们给集合添加和删除操作时,会导致modCount++。

  • 然后当我们用Iterator迭代器遍历集合时,创建集合迭代器的对象时,用一个变量记录当前集合的modCount。例如:int expectedModCount = modCount;,并且在迭代器每次next()迭代元素时,都要检查 expectedModCount != modCount,如果不相等了,那么说明你调用了Iterator迭代器以外的Collection的add,remove等方法,修改了集合的结构,使得modCount++,值变了,就会抛出ConcurrentModificationException。

🌞下面以AbstractList和ArrayList.Itr迭代器为例进行源码分析:

🌞AbstractList类中声明了modCount变量:

        protected transient int modCount = 0;

💡modCount是这个list被结构性修改的次数。结构性修改是指:改变list的size大小,或者,以其他方式改变他导致正在进行迭代时出现错误的结果。

💡这个字段用于迭代器和列表迭代器的实现类中,由迭代器和列表迭代器方法返回。如果这个值被意外改变,这个迭代器将会抛出 ConcurrentModificationException的异常来响应:next,remove,previous,set,add 这些操作。在迭代过程中,他提供了fail-fast行为而不是不确定行为来处理并发修改。

💡子类使用这个字段是可选的,如果子类希望提供fail-fast迭代器,它仅仅需要在add(int, E),remove(int)方法(或者它重写的其他任何会结构性修改这个列表的方法)中添加这个字段。调用一次add(int,E)或者remove(int)方法时必须且仅仅给这个字段加1,否则迭代器会抛出伪装的ConcurrentModificationExceptions错误。如果一个实现类不希望提供fail-fast迭代器,则可以忽略这个字段。

Arraylist的Itr迭代器:

private class Itr implements Iterator {    int cursor;          int lastRet = -1;     int expectedModCount = modCount;//在创建迭代器时,expectedModCount初始化为当前集合的modCount的值    public boolean hasNext() {        return cursor != size;    }    @SuppressWarnings("unchecked")    public E next() {        checkForComodification();//校验expectedModCount与modCount是否相等        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];    }    final void checkForComodification() {        if (modCount != expectedModCount)//校验expectedModCount与modCount是否相等            throw new ConcurrentModificationException();//不相等,抛异常    }}
转载请注明:文章转载自 http://www.konglu.com/
本文地址:http://www.konglu.com/it/1097401.html
免责声明:

我们致力于保护作者版权,注重分享,被刊用文章【Java--Iterator迭代器(集合的遍历)】因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理,本文部分文字与图片资源来自于网络,转载此文是出于传递更多信息之目的,若有来源标注错误或侵犯了您的合法权益,请立即通知我们,情况属实,我们会第一时间予以删除,并同时向您表示歉意,谢谢!

我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2023 成都空麓科技有限公司

ICP备案号:蜀ICP备2023000828号-2