Java中的Iterable与Iterator详解版权声明

原创
小哥 3年前 (2022-10-27) 阅读数 42 #大杂烩

在Java在这种情况下,我们可能是对的。List通过以下方式遍历集合:

List list = new ArrayList<>();
list.add(5);
list.add(23);
list.add(42);
for (int i = 0; i < list.size(); i++) {
    System.out.print(list.get(i) + ",");
}

Iterator it = list.iterator();
while (it.hasNext()) {
    System.out.print(it.next() + ",");
}

for (Integer i : list) {
    System.out.print(i + ",");
}

第一个是平凡for循环,第二个是迭代器遍历,第三个是for each循环后两种方法涉及Java中的iterator和iterable对象,让我们看看这两个对象之间的区别以及如何在自定义类中实现它们。for each循环。

Iterator与Iterable

iterator为Java中的迭代器对象能够。List基础依赖项迭代地遍历这样的集合。和iterable该接口定义了返回。iterator其方法是iterator在实现的同时,iterable接口的类可以支持for each循环。

iterator内部细节

jdk中Iterator该接口的主要方法如下:

public interface Iterator {
    boolean hasNext();
    E next();
}

iterator以上两种方法定义了迭代访问集合的方法,具体实现取决于不同的实现类,具体的集合类实现。Iterator方法来实现迭代。

可以发现,在List未在中实现Iterator接口,但实现的Iterable界面。进一步观察Iterable可以发现该接口的源代码只返回了一个。Iterator对象。

public interface Iterable {
  Iterator iterator();
}

因此,我们可以使用以下方式List迭代(通过调用。iterator()方法)

Iterator it = list.iterator();
while (it.hasNext()) {
    System.out.print(it.next() + ",");
}

同时,也实现了这一点。Iterable也可以使用该接口。for each循环。

for each原理

其实for each循环的内部也依赖于Iterator迭代器,只是Java提供语法糖,Java编译器会将其转换为Iterator迭代器方式遍历。我们对以下内容感兴趣for each用于反编译的循环:

for (Integer i : list) {
       System.out.println(i);
   }

反编译后:

Integer i;
for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(i)){
        i = (Integer)iterator.next();        
    }

可以看到Java的for each制作了增强环路iterator以迭代器的方式实现。

深入探讨Iterable与Iterator关系

有一个问题,为什么不直接hasNext(),next()方法放在Iterable接口,其他类可以直接实现吗?

原因是某些集合类可能有多个遍历方法来实现Iterable然后,该类可以实现多个Iterator内部类,如 LinkedList 中的 ListItrDescendingIterator 这两个内部类分别实现双向遍历和逆序遍历。通过返回不同的 Iterator 实现不同的遍历方法,更加灵活。如果合并这两个接口,则无法返回不同的 Iterator 实现类。ListItr相关源代码如下:

public ListIterator listIterator(int index) {
        checkPositionIndex(index);
        return new ListItr(index);
    }

    private class ListItr implements ListIterator {
        ...
        ListItr(int index) {
            // assert isPositionIndex(index);
            next = (index == size) ? null : node(index);
            nextIndex = index;
        }

        public boolean hasNext() {
            return nextIndex < size;
        }
        ...

如上所示,您可以调用。 list.listIterator() 方法返回iterator迭代器( list.iterator() 仅其默认实现)

DescendingIterator 源代码如下:

public Iterator descendingIterator() {
        return new DescendingIterator();
    }
    private class DescendingIterator implements Iterator     {
        private final ListItr itr = new ListItr(size());
        public boolean hasNext() {
            return itr.hasPrevious();
        }
        public E next() {
            return itr.previous();
        }
        public void remove() {
            itr.remove();
        }
    }

它也可以通过。 list.descendingIterator() 使用迭代器。

实现您自己的迭代器。

我们现在有了一个定制类。ArrayMap,现在,如果按如下方式完成for each遍历:

ArrayMap am = new ArrayMap<>();
am.put("hello", 5);
am.put("syrups", 10);

for (String s: am) {
   System.out.println(s);
}

因为我们还没有实现hashNext和next抽象方法,因此它无法被遍历。

自定义迭代器类

我们首先定制一个迭代器类实现。hashNext和next方法,以及ArrayMap对于内部类,相关代码如下:

public class KeyIterator implements Iterator {
        private int ptr;

        public KeyIterator() {
            ptr = 0;
        }

        @Override
        public boolean hasNext() {
            return (ptr != size);
        }

        @Override
        public K next() {
            K returnItem = keys[ptr];
            ptr += 1;
            return returnItem;
        }
    }

你可以在里面看到我们next中指定的遍历规则基于ArrayMap的key值被遍历。通过上面的迭代器类,我们可以使用它。iterator该方法在外部遍历它,遍历代码如下:

ArrayMap am = new ArrayMap<>();
am.put("hello", 5);
am.put("syrups", 10);
ArrayMap.KeyIterator ami = am.new KeyIterator();
while (ami.hasNext()) {
    System.out.println(ami.next());
}

如上所示,通过创建KeyIterator对象是迭代访问的(请注意外部类如何创建内部类对象)。

支持for each循环

它还不能得到支持。for each循环访问,因为我们尚未实现iterable接口,先入ArrayMap中实现Iterable接口:

public class ArrayMap implements Iterable {

    private K[] keys;
    private V[] values;
    int size;

    public ArrayMap() {
        keys = (K[]) new Object[100];
        values = (V[]) new Object[100];
        size = 0;
    }
  ....
}

然后重写iterator()方法,其中我们返回我们自己的迭代器对象。(iterator)

@Override
    public Iterator iterator() {
        return new KeyIterator();
    }

请注意我们的习俗KeyIterator类必须实现Iterator接口,否则在iterator()该方法中返回的类型不匹配。

总结与感悟

(1)学会深入思考,一点一点地剥开茧,多想想为什么会实现这一点。许多问题并不像你想象的那么复杂。

(2当你遇到疑虑时,不要放弃。这是提升自我的最好机会。当你遇到困难时,你会在解决的过程中挖掘出很多相关的东西。

参考资料:

(1) CS61B

(2) for each实现原理

(3) Iterable与iterator区别 欢迎来华工作一至五年Java工程师之友加入Java群: 891219277
集团内免费Java架构学习材料(高可用性、高并发性、高性能和分布式、Jvm性能调整、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx(如多个知识点的结构)合理利用自己的每一分钟、每一秒来学习提高自己,不要再用它了"没有时间把自己的懒惰藏在思想里!趁着年轻,努力奋斗,给自己一个未来的解释!

版权声明

所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除

热门