容器(三):ListIterator和Iterator辨析与源码分析

容器(三):ListIterator和Iterator辨析与源码分析

标签: Java编程思想


凡是实现了Collection接口的集合类,都有一个Iterator方法,用于返回一个实现了Iterator接口的对象。但是java集合中还有一个迭代器ListIterator,在使用List、ArrayList、LinkedList和Vector的时候可以使用。这两种迭代器有什么区别呢?下面我们详细分析。

游标

迭代器 没有当前所在元素一说,它只有一个游标(cursor)的概念,这个游标总是在元素之间,比如这样:
游标

也就是说长度为 N 的集合会有 N+1 个游标的位置。

Iterator源码分析

关于Iterator在《容器(一):容器双雄之Collection》中介绍过,话不多说,直接上源码:

package java.util;

import java.util.function.Consumer;

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());
    }
}

default关键字

首先,我们看到Iterator接口代码比较简短。但是我们仔细一看,发现里面居然有方法有方法体?

我们知道,在jdk1.8之前,接口只提供了形式,而未提供任何具体实现,并且实现该接口的实现类必须要实现该接口的所有方法。但是在Java 8 中引入了引入了一个新的概念,叫做default方法

default方法是在java8中引入default关键字,也可称为虚拟扩展方法。是指: 在接口内部包含了一些默认的方法实现 (也就是接口中可以包含方法体,这打破了Java之前版本对接口的语法限制),从而使得接口在进行扩展的时候,不会破坏与接口相关的实现类代码。

方法分析

Iterator,主要方法有四种:

  • hasNext():迭代器中是否还有下一个元素,若有则返回true。
  • next():返回迭代器中的下一个元素,并移动游标。
  • remove():删除迭代器新返回的元素。
  • forEachRemaining(Consumer<? super E> + action):使用Lambda表达式进行遍历(现在我也不知道啥是Lambda,Java 8 的新特性,以后再说吧),为每个剩余元素执行给定的操作,直到所有的元素都已经被处理或行动将抛出一个异常

使用迭代器next()方法获得的元素是一个集合中对应元素的深拷贝,如果对迭代变量进行修改是不会修改集合中的原数据的。

同样,也不能直接在迭代过程中使用Collection接口中的remove()等方法对集合进行修改,因为迭代器已经锁定住集合了,强行修改会抛出异常!只能用Iterator接口的专用修改集合元素的方法修改才是正确的,就像上面的Iterator.remove()方法:

public class Test {  
    public static void main(String[] args) {  
        Collection collection = new ArrayList(); // ArrayList是Collection的一个实现类,默认元素类型为Object  

        Iterator it = collection.iterator();  
        while (it.hasNext()) {  
            Type t = it.next(); // 迭代值(数据视图)  
            对t进行操作;  
            collection.remove(); 
            // 错误!!在迭代过程中使用非迭代器方法对集合进行修改会直接抛出异常!!  
        }  
    }  
}  

可以看到,Iterator迭代的“集合”是真正集合的视图,视图和真实数据之间是一一映射的关系,如果此时使用Collection接口中的方法相当于对真实数据进行修改,会导致真实数据和映像之间不一致,因此会抛出异常。

Iterator中的修改方法可以保证这种映射的一致性,即迭代器先对视图进行修改,然后将视图的修改更新到真实数据,但是反向就是无效的因为映像自己是知道关联的是哪个真实数据,但是真实数据本身不知道有哪些映像和我关联的即真实数据永远是被动的,而映像是主动的!

ListIterator介绍

列表迭代器,允许按任一方向遍历列表、迭代期间修改列表,并获得迭代器在列表中的当前位置。

ListIterator 没有当前元素;它的游标位置始终位两个元素之间。长度为 n 的列表的迭代器有 n+1 个可能的指针位置,如下面的插入符举例说明:

注意remove()set(Object) 方法不是根据游标位置定义的;它们是根据对调用 next()previous() 所返回的最后一个元素的操作定义的。

ListIterator源码分析

ListIterator 继承自 Iterator 接口,在 Iterator 的基础上增加了一些内容,话不多说上源码:

package java.util;

public interface ListIterator<E> extends Iterator<E> {

    boolean hasNext();

    E next();

    boolean hasPrevious();

    E previous();

    int nextIndex();

    int previousIndex();

    void remove();

    void set(E e);

    void add(E e);
}

方法分析

介绍一下新来的几个方法:

  • oid hasPrevious():判断游标是否有前一个元素;

  • Object previous():返回游标前面的元素,同时游标前移一位。游标前没有元素就报 java.util.NoSuchElementException 的错,所以使用前最好判断一下;

  • int nextIndex():返回游标后边元素的索引位置,初始为 0 ;遍历 N 个元素结束时为 N;

  • int previousIndex(): 返回游标前面元素的位置,初始时为 -1,同时报 java.util.NoSuchElementException 错;

  • void add(E):在游标 前面 插入一个元素
    注意,是前面;

  • void set(E):更新迭代器最后一次操作的元素为 E,也就是更新最后一次调用 next() 或者 previous() 返回的元素。
    注意,当没有迭代,也就是没有调用 next() 或者 previous() 直接调用 set 时会报 java.lang.IllegalStateException 错;

  • void remove(): 删除迭代器最后一次操作的元素,注意事项和 set 一样。

ListIterator 的实现

java.util包和 java.util.concurrent包下使用。其具体具体的实现也是在其子接口下完成。

返回 ListIterator 的 java.util 中的方法:

  • ListIterator<E> AbstractList.listIterator():返回此列表元素的列表迭代器(按适当顺序)。
  • ListIterator<E> List.listIterator():返回此列表元素的列表迭代器(按适当顺序)。
  • ListIterator<E> LinkedList.listIterator(int index):返回此列表中的元素的列表迭代器(按适当顺序),从列表中指定位置开始。
  • abstract ListIterator<E> AbstractSequentialList.listIterator(int index):返回在此列表中的元素上进行迭代的列表迭代器(按适当顺序)。
  • ListIterator<E> AbstractList.listIterator(int index):返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。
  • ListIterator<E> List.listIterator(int index):返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。

返回 ListIterator 的 java.util.concurrent 中的方法:

  • ListIterator<E> CopyOnWriteArrayList.listIterator():返回此列表元素的列表迭代器(按适当顺序)。
  • ListIterator<E> CopyOnWriteArrayList.listIterator(int index):返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。

ListIterator 的代码测试

package char16;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

/**
 * Created by japson on 8/2/2017.
 */
public class ListIteratorTest {
    public static void main(String[] args) {
        List<String> lists = new ArrayList<String>();
        lists.add("1A");
        lists.add("2B");
        lists.add("3C");
        lists.add("4D");
        lists.add("5F");
        lists.add("6F");
        lists.add("7G");

        ListIterator<String> iterator = lists.listIterator();
        System.out.println("向后遍历");
        //向后遍历
        while(iterator.hasNext()){
            System.out.print(iterator.next()+" ");
        }

        System.out.println();
        System.out.println("此时游标iterator指向最后一个元素的后面,因此最后一个元素为游标的前置位:" + iterator.previous());
        System.out.println();

        //此时游标指向7G和6G之间,可以向前遍历
        System.out.println("iterator.previous()使得游标前进一位指向7G和6F之间,向前遍历");
        while(iterator.hasPrevious()){
            System.out.print(iterator.previous()+" ");
        }
        System.out.println();

        System.out.println();
        System.out.println("从指定位置(下标2)向前遍历");
        //从指定位置向前遍历
        iterator = lists.listIterator(2);
        while(iterator.hasPrevious()){
            System.out.print(iterator.previous()+" ");
        }

        System.out.println();
        System.out.println();
        iterator = lists.listIterator();
        //替换元素,如何一边替换一边输出?
        while(iterator.hasNext()){
            String str = iterator.next();
            iterator.set("H:"+str); //void方法,不能赋值,也没法输出
            System.out.print("替换" + " ");
        }
        System.out.println();

        System.out.println();
        System.out.println("通过替换,游标已经移到队尾了,倒序输出");
        while(iterator.hasPrevious()){
            System.out.print(iterator.previous()+" ");
        }
    }
}

输出:

向后遍历
1A 2B 3C 4D 5F 6F 7G 
此时游标iterator指向最后一个元素的后面,因此最后一个元素为游标的前置位:7G

iterator.previous()使得游标前进一位指向7G和6F之间,向前遍历
6F 5F 4D 3C 2B 1A 

从指定位置(下标2)向前遍历
2B 1A 

替换 替换 替换 替换 替换 替换 替换 

通过替换,游标已经移到队尾了,倒序输出
H:7G H:6F H:5F H:4D H:3C H:2B H:1A 

ListIterator和Iterator的相同点

都是迭代器,当需要对集合中元素进行遍历不需要干涉其遍历过程时,这两种迭代器都可以使用。

ListIterator和Iterator的不同点

  1. 使用范围不同,Iterator可以应用于所有的Collection的子类型。而ListIterator只能用于List及其子类型。

  2. ListIterator有add方法,可以向List中添加对象,而Iterator不能。

  3. ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator不可以。

  4. ListIterator可以定位当前索引的位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。

  5. 都可实现删除操作,但是ListIterator可以实现对象的修改,set()方法可以实现。Iterator仅能遍历,不能修改。

ps:用心学习,喜欢的话请点赞 (在左侧哦)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值