详解Iterable接口与Iterator接口异同与使用
保留所有版权,请引用而不是转载本文(原文地址 https://yeecode.top/blog/61/ )。
Iterable接口与Iterator接口是大家经常接触到的两个接口,它们都代表与迭代操作相关的能力。
Iterator单词的意思是“迭代器”,Iterable单词的意思是“可迭代的”。如果一个类是迭代器,那基于它可以实现迭代操作;而如果一个类能够给出一个迭代自身内元素的迭代器,那它就是可迭代的。
因此,Iterable接口非常简单,主要定义了一个Iterator<T> iterator()
抽象方法用以返回一个Iterator对象(在Jdk 1.8中增加了forEach方法和spliterator方法)。
Iterator接口表示一个针对集合的迭代器,Iterator接口定义了迭代器最重要的方法:
boolean hasNext()
:判断当前迭代中是否还有未迭代的元素。E next()
:返回迭代中的下一个元素。default void remove()
:从迭代器指向的集合中移除迭代器返回的最后一个元素。默认情况下不支持此操作,因为很容易造成迭代混乱。
在编程开发中,Iterable接口与Iterator接口经常被用到。例如我们常用的for-each
就是基于这两个接口实现的。例如下面代码所示的for-each
操作:
List<User> userList = new ArrayList<>();
for (User user : userList) {
System.out.println(user);
}
经过编译后在class文件中变成了如下所示的样子。
List<User> userList = new ArrayList();
Iterator var2 = userList.iterator();
while(var2.hasNext()) {
User user = (User)var2.next();
System.out.println(user);
}
这是因为for-each
是一个语法糖操作,会由编译器在编译阶段帮我们转化为基本语法。于是我们使用for-each
操作对List中的元素进行遍历时,List作为Iterable接口的子类先通过iterator方法给出一个Iterator对象,然后基于Iterator对象实现了List中所有元素的遍历。
语法糖是编程语言中一种简化的语法,它能够使得代码更加简洁。对于Java语言而言,JVM并不识别语法糖中的语句,因此这些语句会在编译阶段被还原成基本的语句。
要想查看一段java代码在class文件中的真实形态,最简单的方法是使用集成开发软件找到target目录下对应的class文件后查看。也可以使用自己使用javac命令编译后使用相关工具打开对应的class文件查看。
最后我们再总结一下,Iterable接口表征一个类是可迭代的,Iterator接口表征一个类是迭代器:
- 如果一个类,能够给出一个迭代器(通过iterator方法)用来对某个集合中的元素进行迭代,那么这个类可以继承Iterable接口。
- 如果一个类本身就是一个迭代器,能够对某个集合展开迭代操作,那么这个类可以继承Iterator接口。
以上内容均参考《通用源码阅读指导书——MyBatis源码详解》一书。
阅读源码确实对编程能力的提升有很大的帮助。《通用源码阅读指导书——MyBatis源码详解》是一本以MyBatis的源码为实例讲述源码阅读方法的书籍,并且附带有示例项目源码,MyBatis的全中文注解。书籍还总结了大量的编程知识和架构经验,对提升编程和架构能力十分有用。
而MyBatis中有很多地方用到了Iterable接口与Iterator接口,我们参照《通用源码阅读指导书——MyBatis源码详解》了解下其具体使用。
在使用MyBatis进行数据库查询时,经常会查询到大量的结果。如下面代码所示的例子中,我们查询到了大量的User对象,并使用List接受了这些对象。
List<User> userList = userMapper.queryUserBySchoolName(userParam);
但有些时候,我们希望逐一读入和处理查询结果,而不是一次读入整个结果集。因为前者能够减少对内存的占用,这在处理大量的数据时会显得十分必要。游标就能够帮我们实现这一目的,它支持我们每次从结果集中取出一条结果。
MyBatis中使用游标进行查询非常简单,映射文件不需要任何的变动,只需要在映射接口中标明返回值类型是Cursor即可,如下所示。
Cursor<User> queryUserBySchoolName(User user);
然后便可以用下面的方式来接收和处理结果。
UserMapper userMapper = session.getMapper(UserMapper.class);
User userParam = new User();
userParam.setSchoolName("Sunny School");
Cursor<User> userCursor = userMapper.queryUserBySchoolName(userParam);
for (User user : userCursor) {
System.out.println("name : " + user.getName() + " ; email : " + user.getEmail());
}
其最终实现就是使用迭代器Iterator。其涉及的具体的MyBatis源码我们不再展开,大家可以参考《通用源码阅读指导书——MyBatis源码详解》的“第21章 cursor包”章节。
最后,我是高级架构师易哥,这里是架构研究所。真心希望本文能让大家有所收获。
欢迎关注我们,我会偶尔出没分享软件架构和编程相关的干货知识。
可以访问个人知乎阅读更多文章:易哥(https://www.zhihu.com/people/yeecode),欢迎关注。