jvm学习——有关ListIterator和Iterator的理解——丑九怪

迭代器存在的意义

  • java中容器类有很多,大部分内部结构都不太一样,当我们需要对我们所使用的容器类进行遍历时,就不可避免的需要先知道这个容器的内部结构,才可以的对它进行遍历操作。可是有些容器内部结构复杂,这就给遍历容器带来了困难。所以jdk中提出了迭代器的思想:所有容器都会实现迭代器,在各自容器内部实现遍历过程并对外提供遍历容器的手段。对外方便的同时,也保护了容器的内部结构不被外面知道。这就是我理解的跌迭代器。

有关Iterator

首先先看一下Iterator接口的内容

public interface Iterator<E> {

    boolean hasNext();
    
    E next();
    
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }
    
    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}
解释Iterator中的方法
  • hasNext();方法
    这个方法返回boolean,用来判断容器内是否还有下一个元素
  • next();方法
    返回了一个E,意思是返回容器中的下一个元素
  • remove();方法
    删除容器中的最后一个元素,但是在接口中的默认处理方式时抛出一个“不支持操作”异常
  • forEachRemaining(Consumer<? super E> action);方法
    这个方法是jdk1.8新加入的方法,参数是一个Consumer(有关Consumer可以参考我的博文有关1.8中的函数式编程接口——丑九怪),这个方法的作用,就是根据外面在action中所设置的过程(函数),通过对容器的遍历,实现对容器内对应元素的造作。如果还是不太明白,请参考完上面的博文再来研究这个方法。

有关ListIterator

同样先看一下接口内容

public interface ListIterator<E> extends Iterator<E> {
    // Query Operations 查询操作
    
    boolean hasNext();

    E next();
    
    boolean hasPrevious();

    E previous();

    int nextIndex();

    int previousIndex();
    
    // Modification Operations 修改造作

    void remove();

    void set(E e);

    void add(E e);
}

很明显可以得到两个信息
1、ListIterator接口继承于Iterator接口
2、ListIterator中的操作分为两类查询操作修改操作

解析ListIterator中的方法
有关查询的方法
  • hasNext();方法
    这个方法返回boolean,判断是否还有下一个元素
  • next();方法
    这个方法返回容器内的下一个元素
  • hasPrevious();方法
    他和hasNext()方法刚好相反,他检查是否有上一个元素,相当于倒序
  • previous();方法
    这个方法和next()方法相反,返回容器内的上一个元素
  • nextIndex()方法
    返回下一个元素的下标
  • previousIndex();方法
    返回上一个元素的下标
有关修改的方法
  • remove();方法
    移除容器中的最后一个元素,容器大小发生改变。这个方法覆盖了Iterator中的remove()方法
  • set(E e);方法
    重新给容器中最后一个元素赋值为e,但是容器的结构以及大小没有发生改变
  • add(E e);方法
    在容器最后追加一个元素,改变了容器大小

Iterator和ListIterator的异同

通过上面的总结,他们的异同已经明了:

  • Iterator和ListIterator都可以对容器进行遍历操作,不同的地方在于ListIterator提供了倒序便利的手段——hasPrevious()方法和previous()方法的组合使用就可以做到倒序遍历
  • Iterator本身对容器大小结构的修改关闭,它的remove()方法会抛出异常。可以这么理解,就是不允许改变容器的大小结构。而对于ListIterator来说,他在继承并修改Iterator接口的remove()方法同时,还提供了add(E e)方法,就是说ListIterator允许对容器进行大小结构上的修改

Iterator和ListIterator在源码中的具体实现以及解析

在源码中的迭代器会根据容器的类型有各自的实现方式,这里以ArrayList为例,简要说明一下ArrayList中迭代器的工作原理。由于代码较长,这里只贴出部分较核心内容。

  • 在ArrayList中有一个字段modCount,每当调用了ArrayList中能够使容器发生改变的方法,就会将modCount加一
  • Iterator接口在ArrayList中以被一个叫做Itr的内部类实现,remove()方法被重写,本质上是调用Arra中的remove()方法。在内部类Itr中,有两个成员非常关键,expectedModCountlastRet。expectedModCount被赋初值与modCount相等,lastRet赋初值为-1.
  • 一般情况下,我们用以下方式遍历
List<Integer> strList = new ArrayList<>();
init(strList); // 这里初始化的代码只是简单地向list中添加元素
Iterator<Integer> intIterator = strList.iterator(); // 取得Iterator
while (intIterator.hasNext()) {
    System.out.println(intIterator.next());
}

在此过程中,任何其他线程对此容器内元素的任何操作都是不允许的。这一点体现在next()方法中(事实上,凡是能使容器内容发生改变的方法都可以体现)。在方法调用最开始,会先检查expectedModCount和modCount是否相等。如果存在其他线程对这个容器进行了操作,会相应地修改modCount的值,导致expectedModCount和modCount不相等,这会让方法抛出ConcurrentModificationException异常。这样做保证了迭代器的安全运行。

 public E next() {
        checkForComodification();  // 这个方法检查expectedModCount和modCount是否相等
   ...... // 省略的代码
        }
 final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
  • 对于lastRet,一方面他是迭代器中标明当前位置的“指针”,另一方面,我认为他有类似标记的功能。每当执行使容器结构发生改变的方法时,会同时将lastRet改为-1,再次调用这种方法时,方法内部会先判断lastRet是否为-1,如果lastRet的值为-1,代表容器结构发生了改变,无法确定当前所遍历到的元素。这种情况下会抛出IllegalStateException()异常。而next()方法会重新给lastRet赋值为遍历到的最后一个元素的下标,也就是重新校准lastRet的值,这种机制也保证了迭代器的正常运行。remove()方法中有相关代码,这里贴出:
public void remove() {
     if (lastRet < 0)  // 判断lastRet的值是否为-1
         throw new IllegalStateException();
     ...... // 省略的代码
}
public E next() {
     ...... // 省略的代码
     cursor = i + 1;
     return (E) elementData[lastRet = i]; // 这里给lastRet赋值为当前元素下标,相当于重新校准
        }

如果没有这个机制,同时外面不停地对集合用迭代器进行删除,迭代器内部所记录的下标数据就会出现混乱

  • ListIterator中的实现方式和Iterator中的大同小异,理解了上述说法,自学ListIterator并非难事
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值