区分Java中的Iterable与Iterator接口

2023-11-12

Java中的Iterable与Iterator详解

在Java中,我们可以对List集合进行如下几种方式的遍历:

List<Integer> 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<E> {
	boolean hasNext();
  	E next();
}

iterator通过以上两个方法定义了对集合迭代访问的方法,而具体的实现方式依赖于不同的实现类,具体的集合类实现Iterator接口中的方法以实现迭代。

可以发现,在List中并没有实现Iterator接口,而是实现的Iterable接口。进一步观察Iterable接口的源码可以发现其只是返回了一个Iterator对象。

public interface Iterable<T> {
  Iterator<T> 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<E> listIterator(int index) {
        checkPositionIndex(index);
        return new ListItr(index);
    }

    private class ListItr implements ListIterator<E> {
		...
        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<E> descendingIterator() {
        return new DescendingIterator();
    }
    private class DescendingIterator implements Iterator<E> 	{
        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<String, Integer> 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<K> {
        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<String, Integer> 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<K, V> implements Iterable<K> {

    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<K> iterator() {
        return new KeyIterator();
    }

注意我们自定义的KeyIterator类必须要实现Iterator接口,否则在iterator()方法中返回的类型不匹配。

总结与感想

(1)学会深入思考,一点点抽丝剥茧,多想想为什么这样实现,很多问题没有自己想象中的那么复杂。

(2)遇到疑惑不放弃,这是提升自己最好的机会,遇到某个疑难的点,解决的过程中会挖掘出很多相关东西。

参考资料:

(1)CS61B

(2)for each实现原理

(3)Iterable与iterator区别

更多内容请移步: http://litexy.com

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

区分Java中的Iterable与Iterator接口 的相关文章

随机推荐

  • openssl hmac源码分析

    hmac 原理 HMAC 用于保护消息的完整性 它采用摘要算法对消息 填充以及秘密密钥进行混合 运算 在消息传输时 用户不仅传送消息本身 还传送 HMAC 值 接收方接收数据后也进 行 HMAC 运算 再比对 MAC 值是否一致 由于秘密密
  • 【ORACLE】ora-12519错误分析解决

    首先检查process和session的使用情况 在sqlplus里面查看 SQL gt show parameter processes NAME TYPE VALUE aq tm processes integer 0 db write
  • gdb常用的调试方法

    1 安装gdb yum install gdb 2 打印线程的堆栈 1 ps afx 查看进程id 2 attach 正在运行的进程 gdb debugme pid 3 set logging file tmp test txt 设置操作g
  • hadoop :java.io.FileNotFoundException: File does not exist:

    点击打开链接转自 http blog 163 com silver9886 126 blog static 35971862201441134010403 1 用hadoop的eclipse插件下M R程序的时候 有时候会报 Excepti
  • JSP pagecontext对象的简介说明

    转自 JSP pagecontext对象的简介说明 下文笔者将讲述pagecontext对象的简介说明 如下所示 pageContext对象的简介 pageContext对象是javax servlet jsp PageContext类的实
  • 解决问题记录16:jar包冲突解决

    当项目中jar遇到冲突问题时 一般我的处理方式就是 比较冲突jar 找出冲突的地方 自己取舍排除即可
  • 实现图片验证码与手机短信验证码

    实现图片验证码与手机短信验证码 1 HTML 代码
  • git clone失败:Cloning into... fatal: unable to access... error setting certificate verify locations

    参考链接 others How to solve gnutls handshake failed error when doing git clone from github com Errors Cloning into GlobalTr
  • Spring5框架新功能

    文章目录 Spring5框架新功能 Spring5框架新功能 1 整个Spring5框架的代码基于java8 运行时兼容JDK9 许多不建议使用的类和方法在代码库中删除 2 Spring5框架自带了通用的日志封装 1 Spring5已经移除
  • ant design pro开发环境搭建

    命令行窗口使用管理员身份打开 以免出现其他不可意料的错误 npm create umi 2 依次选择 gt ant design pro gt Prov4 gt TypeScript gt simple gt antd 4 3 npm in
  • 刷脸支付服务商巧借东风顺势而为

    银行可以从自身占优势的园区场景切入 区别于支付宝和微信市场策略 差异化的快速占领市场 我们通常说的园区 包括了校园 景区 办公楼 以及各类工业园区和行政园区 前期这块市场主要都是由传统银行作为收单机构提供服务 疫情过后 整体刷脸支付将再起步
  • 2022年浙江省中职组“网络空间安全”编码信息获取

    什么是wireshark wiresharek wire0078 pcap数据包 wiresharek Wireshark 前称Ethereal 是一个网络封包分析软件 网络封包分析软件的功能是检索取网络封包 并同时显示出最详细的网络封包数
  • 图像算法工程师常考的面试问题附回答

    什么是图像滤波 介绍一下常用的图像滤波器有哪些 什么是卷积神经网络 CNN 它的结构是什么样的 为什么在图像处理中表现出色 什么是图像分割 介绍一下图像分割的方法和应用场景 什么是图像匹配 介绍一下图像匹配的方法和应用场景 什么是直方图均衡
  • 算法提高 最长滑雪道(动态规划 + Dfs)

    试题 算法提高 最长滑雪道 资源限制 时间限制 1 0s 内存限制 256 0MB 问题描述 小袁非常喜欢滑雪 因为滑雪很刺激 为了获得速度 滑的区域必须向下倾斜 而且当你滑到坡底 你不得不再次走上坡或者等待升降机来载你 小袁想知道在某个区
  • linux下解决bash: syntax error near unexpected token `(' 的错误

    linux下解决bash syntax error near unexpected token 的错误 1 只需加上 就行 转义括号为可读 2 在括号两端加上 双引号 如 1 rm f HIBIKI API 1 jarrm f HIBIKI
  • unity中脚本添加不了的原因及解决方案

    1 删除挂不上去的脚本 重新添加这个脚本 2 看看有没有其他的脚本编译错误 有的话修改其他脚本 3 检测脚本 名字与雷明是否一致 不一致的话修改类名 使得脚本与类名一致 4 重启下unity的编辑器 有时候unity会卡顿 需要重新启动
  • 电路初级基本原理总结

    电路初级基本原理总结 电荷 正电荷和负电荷 带了电 荷 摩擦过的物体有了吸引物体的轻小物体的性质 电荷间的相互作用规律 同种电荷相互排斥 异种电荷相互吸引 元电荷 e 一个电子所带的电荷量 e 1 6 10 19 C 电荷量 电荷的多少 单
  • Ubuntu 安装docker、docker-compose

    一 更新Ubuntu源 本文章更新阿里云源 可跳过 1 备份原始源 sudo cp etc apt source list etc apt source list backup 2 修改源文件 sudo vim etc apt source
  • SQL数据库权限管理-10个数据库角色

    为便于管理数据库中的权限 SQL 数据库提供了服务器角色 数据库角色 用户等来划分不同用户拥有的权限差异 今天给大家介绍数据库角色对应的权限 数据库级角色 存在两种类型的数据库级角色 数据库中预定义的 固定数据库角色 可以创建的 用户定义的
  • 区分Java中的Iterable与Iterator接口

    Java中的Iterable与Iterator详解 在Java中 我们可以对List集合进行如下几种方式的遍历 List