码迷,mamicode.com
首页 > 编程语言 > 详细

JAVA 迭代器

时间:2020-03-13 15:57:30      阅读:86      评论:0      收藏:0      [点我收藏+]

标签:构造   异常   move   私有   判断   cat   ORC   current   strong   

JAVA中的迭代器大多作为私有的内部类实现,不妨我们先以ArrayList为例

先看一下继承树
技术图片

1.IteratorIterable

首先,我们需要先看一下,Iterator接口中,声明了那些方法
注意默认方法有方法体,只不过为了简洁,这里没有贴上代码,而是用分号代替了,请不要过于关注于此

//判断当前迭代器是否指向了最后一个元素
boolean hasNext();
//放回迭代器指向的元素,并将迭代器指向下一个元素
E next();
//移除迭代器刚刚遍历到的元素
default void remove();
//遍历还没有遍历过的元素
default void forEachRemaining(Consumer<? super E> action);

迭代器使用的泛型一般就是外部类

既然说到了Iterator接口,那我们就得看一下Iterable接口了
这两个接口不仅名字相似,实际上也确实息息相关
比如Iterable的第一个方法

//返回一个迭代器(Iterator的实例)
Iterator<T> iterator();

接下来的这个方法用于遍历集合中的元素

default void forEach(Consumer<? super T> action)
//稍后再谈
default Spliterator<T> spliterator();

2.ArrayList中的Itr

private class Itr implements Iterator<E>

由于ArrayList的底层是一个数组,所以迭代器实现很简单,用一个int型数据(cursor)记录一下当前指向(下一个next要返回的)第几个元素就行了
于此同时,还需要一个变量lastRet记录上一个返回的哪个元素,一般情况下,lastRet = cursor - 1,但是,当出现删除时,lastRet记为-1,直到下次执行next()或者forEachRemaining().
lastRet用于避免重复删除,不可以连续调用remove()两次
注意遍历过程中还要不停检查modCount,值得一提的是,Itr自身的remove()并不会引起与modCount相关的异常,因为在执行remove()时,expectedModCount也做了改变

3.ListIterator接口

先看一下ArrayList中,ListIterator的实现类的继承树
技术图片

它的方法如下

和Iterator相同的

boolean hasNext();
E next();
void remove();

少了一个forEachRemaining方法

和以上Iterator相反的是

boolean hasPrevious();
E previous();

由于Iterator只可以向后挪动,所以实际上,ArrayList的相关方法中,没有任何一个方法可以使得cursor变小
ListIterator声明了这两个方法,表明ListIterator在Iterator的基础上,反向遍历
但是没有方法提供了设置遍历的开始是在结尾还是开头

此外还声明了以下方法

//返回如果调用next,应该返回那个下标对应的值
int nextIndex();
//返回如果调用previous,应该返回那个下标对应的值
int previousIndex();
//将当前迭代器的元素更换为e
void set(E e);
//在当前位置新增一个元素
void add(E e);

4.ArrayList中的ListItr

首先要提的就是
ArrayList.listIterator()方法,用于返回一个ListItr,但是这个方法并没有定义在Iterable接口中
同时ArrayList.listIterator(int index)方法,可以指定ListItr的起始位置

在实现listIterator时,相关变量的定义还是没变,cursor记录下一次执行next()要返回的元素下标
set()add()方法,其实就是内部加了一些检测,然后调用了ArrayList的set()add()方法
这两个方法有值得注意的地方
当cursor等于3时,add()执行,其后cursor指向的是3还是4呢
看一下源码

int i = cursor;
//在i位置插入一个元素,i和i之后的元素都往后挪一位
ArrayList.this.add(i, e);
//指向原来指向的元素
cursor = i + 1;
//禁止remove的执行
lastRet = -1;
expectedModCount = modCount;

可以看出,就元素而言,add()方法并不会改变cursor指向的元素,如果想遍历到刚刚插入的元素,就只能使用E previous()方法了

而对于set()方法

public void set(E e) {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        ArrayList.this.set(lastRet, e);
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

可以看到,set()方法基于lastRet,也就是说,和remove一样,lastRet 为-1时是不可用的
那么我们先来总结一下,什么时候lastRet为-1呢

1.迭代器刚刚构造出
2.执行完remove()
3.执行完add()

可以注意到set()并没有更新expectedModCount ,但是实际上,ArrayList.set(int index,E e)本身就没有更新modMount。

5.Spliterator接口

老规矩,看一下ArrayList实现类的继承树
技术图片
相信你一定还记得,Iterable的default Spliterator<T> spliterator()方法,可见实际上这也是一个很重要的接口
这个接口大致就是把一个ArrayList分为几段,以加速并行处理。

我将在学习多线程相关知识后回来补充

6.ArrayList的spliterator

7........

JAVA 迭代器

标签:构造   异常   move   私有   判断   cat   ORC   current   strong   

原文地址:https://www.cnblogs.com/ZGQblogs/p/12486909.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!