天天看点

JDK源码阅读-Iterator接口概述方法Q&A如有不适之处,欢迎留言讨论

概述

查看Collection接口的定义,发现其继承了Iterable接口,而后者又聚合了一个成员类,即Iterator。因此就先瞅瞅这个接口的定义细节。

Iterator,即我们常用的迭代器,取代了老式的Enumeration。能够允许我们在不了解集合序列的底层结构时,轻松遍历并操作序列中的对象。因为创建代价小,因此迭代器也被视为“轻量级”对象。JDK8源码中Iterator接口只包含了4个方法:

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

方法

next()方法

通过反复调用next方法,可以逐个访问集合中的每个元素。但是,如果到达了集合的末尾,next方法将抛出一个

NoSuchElementException

异常。因此next方法需要配合hasNext方法使用。

Iterator迭代器的查找操作和位置变更时紧密联系的。查找一个元素的唯一方法就是调用next,而在执行查找操作的同时,迭代器的位置随之向前移动。因此,应该将java迭代器认为是位于两个元素之间。当调用next时,迭代器就越过下一个元素,并返回刚刚越过的那个元素的引用。 – 《java核心技术》
JDK源码阅读-Iterator接口概述方法Q&amp;A如有不适之处,欢迎留言讨论

hasNext()方法

hasNext主要是用来判断当前迭代器是否还有元素可供访问,以避免上述next方法抛出异常。

remove()方法

用来删除上次调用next方法返回的元素。值得注意的是,next方法和remove方法的调用具有互相依赖的关系。如果调用remove之前没有调用next方法将会抛出

IlleagalStateException

异常。通俗来讲,如果想remove一个元素,则必须先用hasNext判断迭代器是否还有元素可删,如果有则必须再调用next方法“越过”这个元素,最后删除刚刚“越过”的元素。因此,开发时,存在想删除两个元素,就一口气连续调用两次remove方法,明显就是错误的做法。如下:

--------------Error----------
it.remove();
it.remove(); 连续调用两次remove,Eorror

-------------Correct---------
it.remove();
it.next();
it.remove();
           

举例:遍历List

public static void main(String[] args) {
        List<String> xxBrothers = new ArrayList<>();
        xxBrothers.add("王宝强");
        xxBrothers.add("陈羽凡");
        xxBrothers.add("贾乃亮");
        Iterator<String> it = xxBrothers.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
            it.remove();
        }
        System.out.println(xxBrothers.size());
    }
    
---------output---------
王宝强
陈羽凡
贾乃亮
0
           

源码中方法的定义:

default void remove() { // 使用了JDK8的默认方法的新特性
    throw new UnsupportedOperationException("remove");
}
           

forEachRemaining(Consumer)方法

forEachRemaining是在JDK1.8添加的方法,其特点是:Lambda表达式结合迭代器遍历集合吗。其实现如下:

default void forEachRemaining(Consumer<? super E> action) {
    Objects.requireNonNull(action);
    while (hasNext())
        action.accept(next());
}
           

遍历List集合:

public static void main(String[] args) {
    List<String> xxBrothers = new ArrayList<>();
    xxBrothers.add("王宝强");
    xxBrothers.add("陈羽凡");
    xxBrothers.add("贾乃亮");
    Iterator<String> it = xxBrothers.iterator();
    
    it.forEachRemaining(who -> {
        System.out.println(who);
    });

---------output---------
王宝强
陈羽凡
贾乃亮
           

特别的是,不能在lamba表达式修改集合元素

Q&A

为什么要使用迭代器,而不是使用索引方式遍历数组

Iterator设计的初衷是为了把遍历一个集合的API进行统一。无论是Array、LinkList、ArrayList或是其他集合,都可以使用迭代器进行统一遍历,而不用care集合的数据结构和实现细节。一句话:Iterator把一个集合的实现和遍历进行了分离。

Iterator接口的作用

接口是一种规范,因此Iterator接口就是规定了各个集合实现该接口时应该遵循的规范。同时,接口也只是一种规范,至于怎么实现这种规范,每个集合会根据自己内部的数据结构等情况考虑实现方式。如对于remove()方法的实现,ArrayList是调用其本身的remove() 方法删除最后一个位置上的元素,然后元素数量减一即可。而对于LinkList的remove()方法,则更多的是通过指针的移动来实现删除操作。

如有不适之处,欢迎留言讨论