Java基础 -- 深入理解迭代器
目录
在Java基础 -- 持有对象(容器)已经详细介绍到,集合(Collection)的种类有很多种,比如ArrayList、LinkedList、HashSet...。
由于集合的内部结构不同,很多时候可能不知道该怎样去遍历一个集合中的元素。所以为了使对集合内元素的操作更为简单,Java引入了迭代器模式!
一 为什么使用迭代器
迭代器把访问逻辑从不同类型的集合类型中抽取出来,从而避免向外部暴露集合的内部结构。
对于数组我们使用的是下标来进行处理的:
1 int array[] = new int[3];
2 for (int i = 0; i < array.length; i++) {
3 System.out.println(array[i]);
4 }
对ArrayList的处理:
1 List<String> list = new ArrayList<String>();
2 for(int i = 0 ; i < list.size() ; i++){
3 String string = list.get(i);
4 }
对于这两种方式,我们总是都知道它的内部结构,访问代码和集合本身是紧密耦合的,无法将访问逻辑从集合类和客户端代码中分离出来。不同的集合会对应不同的遍历方法,客户端代码无法复用。在实际应用中如何将上面两个集合整合是相当麻烦的。所以才有Iterator,它总是用同一种逻辑来遍历集合。使得客户端自身不需要来维护集合的内部结构,所有的内部状态都由Iterator来维护。客户端不用直接和集合进行打交道,而是控制Iterator向它发送向前向后的指令,就可以遍历集合。
二 迭代器的使用
迭代器通常被称为轻量级对象;创建它的代价小,因此,经常可以看到对迭代器有些奇怪的限制,例如,Java的Iterator只能单向移动,这个Iterator只能用来:
- 使用方法iterator()要求集合返回一个Iterator,Iterator将准备好返回集合的第一个元素;
- 使用next()获取集合中的下一个元素;
- 使用hasNext()检查集合中是否还存在元素;
- 使用remove()将迭代器新近返回的元素删除;
1、Iterator接口
为了观察它的工作方式,让我们来看一个例子:
import java.util.*; public class SimpleIteration { public static void main(String[] args) { //默认从小到大排序 List<Person> pers = new ArrayList<Person>(); Person p1 = new Person("吴定会",50,true); pers.add(p1); Person p2 = new Person("沈艳霞",46,false); pers.add(p2); Person p3 = new Person("张三",17,true); pers.add(p3); Person p4 = new Person("李四",34,false); pers.add(p4); Iterator<Person> it = pers.iterator(); //检查list中是否还有元素 while(it.hasNext()) { //获取list中下一个元素 Person p = it.next(); System.out.print(p + " "); } System.out.println(); it = pers.iterator(); for(int i=0;i<4;i++) { //获取list中下一个元素 it.next(); //将迭代器最新返回的元素删除 it.remove(); System.out.println(i + ":" + pers); } System.out.print(pers); } }
输出结果如下:
1 2 3 4 5 6 | 吴定会- 50 - true 沈艳霞- 46 - false 张三- 17 - true 李四- 34 - false 0 :[沈艳霞- 46 - false , 张三- 17 - true , 李四- 34 - false ] 1 :[张三- 17 - true , 李四- 34 - false ] 2 :[李四- 34 - false ] 3 :[] [] |
现在考虑创建一个display()方法,它不必知晓集合的确切类型:
import java.util.*; public class CrossContainerIteration { public static void display(Iterator<Person> it) { while(it.hasNext()) { Person p = it.next(); System.out.print(p + " "); } System.out.println(); } public static void main(String[] args) { ArrayList<Person> pers = new ArrayList<Person>(); Person p1 = new Person("吴定会",50,true); pers.add(p1); Person p2 = new Person("沈艳霞",46,false); pers.add(p2); Person p3 = new Person("张三",17,true); pers.add(p3); Person p4 = new Person("李四",34,false); pers.add(p4); LinkedList<Person> persLL = new LinkedList<Person>(pers); HashSet<Person> persHS = new HashSet<Person>(pers); display(pers.iterator()); display(persLL.iterator()); display(persHS.iterator()); } }
输出:
1 2 3 | 吴定会- 50 - true 沈艳霞- 46 - false 张三- 17 - true 李四- 34 - false 吴定会- 50 - true 沈艳霞- 46 - false 张三- 17 - true 李四- 34 - false 张三- 17 - true 吴定会- 50 - true 沈艳霞- 46 - false 李四- 34 - false |
可以看到display()方法不包含任何有关它所遍历的集合的类型信息,而这也展示了Iterator的真正威力,能够将遍历集合的操作与集合底层结构分离。正因为如此,我们有时才会说,迭代器统一了对集合的访问方式。
2、ListIterator
ListIterator是一个更加强大的Iterator的子类型,它只能用于各种List类的访问,尽管Iterator只能向前移动,但是ListIterator可以双向移动。它还可以产生相对于迭代器在列表中指向的当前位置的前一个和后一个元素的索引,并且可以使用set()方法替代它访问过的最后一个元素。可以通过调用listIterrator()方法产生一个指向List开始处的ListIterator,并且还可以通过调用listIterator(n)方法创建一个一开始指向列表索引为n的元素处的ListIterator。下面的示例演示了所有这些能力:
import java.util.*; public class ListIteration { public static void main(String[] args) { List<Person> pers = new ArrayList<Person>(); Person p1 = new Person("吴定会",50,true); pers.add(p1); Person p2 = new Person("沈艳霞",46,false); pers.add(p2); Person p3 = new Person("张三",17,true); pers.add(p3); Person p4 = new Person("李四",34,false); pers.add(p4); //从前向后遍历 ListIterator<Person> it = pers.listIterator(); while(it.hasNext()) { System.out.print(it.next() + " "); } System.out.println(); //从后向前遍历 while(it.hasPrevious()) { System.out.print(it.previous() + " "); } System.out.println(); System.out.println(pers); //创建一个开始指向第三个索引元素处的ListIterator it = pers.listIterator(3); while(it.hasNext()) { System.out.println(it.next()); //替换在列表中从位置3开始向前的所有Person对象 it.set(p1); } System.out.println(pers); } }
输出:
1 2 3 4 5 | 吴定会- 50 - true 沈艳霞- 46 - false 张三- 17 - true 李四- 34 - false 李四- 34 - false 张三- 17 - true 沈艳霞- 46 - false 吴定会- 50 - true [吴定会- 50 - true , 沈艳霞- 46 - false , 张三- 17 - true , 李四- 34 - false ] 李四- 34 - false [吴定会- 50 - true , 沈艳霞- 46 - false , 张三- 17 - true , 吴定会- 50 - true ] |
三 迭代器的实现
1、下面让我们看看Java中的Iterator接口是如何实现的
在Java中Iterator为一个接口,它只提供了迭代的基本规则。在JDK中它是这样定义的:对Collection进行迭代的迭代器。迭代器取代了Java Collection Framework中的Enumeration。迭代器与枚举有两点不同:
- 迭代器在迭代期间可以从集合中移除元素。
- 方法名得到了改进,Enumeration的方法名称都比较长。
其接口定义如下:
package java.util; public interface Iterator<E> { boolean hasNext();//判断是否存在下一个对象元素 E next();//获取下一个元素 void remove();//移除元素 }
2、Iterable
Java中还提供了一个Iterable接口,Iterable接口实现后的功能是返回一个迭代器,我们常用的实现了该接口的子接口有:Collection<E>、List<E>、Set<E>等。该接口的iterator()方法返回一个标准的Iterator实现。实现Iterable接口允许对象成为foreach语句的目标。就可以通过foreach语句来遍历你的底层序列。
Iterable接口包含一个能产生Iterator对象的方法,并且Iterable被foreach用来在序列中移动。因此如果创建了实现Iterable接口的类,都可以将它用于foreach中。
Iterable接口的具体实现:
Package java.lang; import java.util.Iterator; public interface Iterable<T> { Iterator<T> iterator(); }
使用迭代器遍历集合:
public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("张三1"); list.add("张三2"); list.add("张三3"); list.add("张三4"); List<String> linkList = new LinkedList<String>(); linkList.add("link1"); linkList.add("link2"); linkList.add("link3"); linkList.add("link4"); Set<String> set = new HashSet<String>(); set.add("set1"); set.add("set2"); set.add("set3"); set.add("set4"); //使用迭代器遍历ArrayList集合 Iterator<String> listIt = list.iterator(); while(listIt.hasNext()){ System.out.println(listIt.next()); } //使用迭代器遍历Set集合 Iterator<String> setIt = set.iterator(); while(setIt.hasNext()){ System.out.println(listIt.next()); } //使用迭代器遍历LinkedList集合 Iterator<String> linkIt = linkList.iterator(); while(linkIt.hasNext()){ System.out.println(listIt.next()); } }
使用foreach遍历集合:
1 2 3 4 5 6 7 8 | List<String> list = new ArrayList<String>(); list.add( "张三1" ); list.add( "张三2" ); list.add( "张三3" ); list.add( "张三4" ); for (String string : list) { System.out.println(string); } |
可以看出使用foreach遍历集合的优势在于代码更加的简洁,更不容易出错,不用关心下标的起始值和终止值。
亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。
日期 | 姓名 | 金额 |
---|---|---|
2023-09-06 | *源 | 19 |
2023-09-11 | *朝科 | 88 |
2023-09-21 | *号 | 5 |
2023-09-16 | *真 | 60 |
2023-10-26 | *通 | 9.9 |
2023-11-04 | *慎 | 0.66 |
2023-11-24 | *恩 | 0.01 |
2023-12-30 | I*B | 1 |
2024-01-28 | *兴 | 20 |
2024-02-01 | QYing | 20 |
2024-02-11 | *督 | 6 |
2024-02-18 | 一*x | 1 |
2024-02-20 | c*l | 18.88 |
2024-01-01 | *I | 5 |
2024-04-08 | *程 | 150 |
2024-04-18 | *超 | 20 |
2024-04-26 | .*V | 30 |
2024-05-08 | D*W | 5 |
2024-05-29 | *辉 | 20 |
2024-05-30 | *雄 | 10 |
2024-06-08 | *: | 10 |
2024-06-23 | 小狮子 | 666 |
2024-06-28 | *s | 6.66 |
2024-06-29 | *炼 | 1 |
2024-06-30 | *! | 1 |
2024-07-08 | *方 | 20 |
2024-07-18 | A*1 | 6.66 |
2024-07-31 | *北 | 12 |
2024-08-13 | *基 | 1 |
2024-08-23 | n*s | 2 |
2024-09-02 | *源 | 50 |
2024-09-04 | *J | 2 |
2024-09-06 | *强 | 8.8 |
2024-09-09 | *波 | 1 |
2024-09-10 | *口 | 1 |
2024-09-10 | *波 | 1 |
2024-09-12 | *波 | 10 |
2024-09-18 | *明 | 1.68 |
2024-09-26 | B*h | 10 |
2024-09-30 | 岁 | 10 |
2024-10-02 | M*i | 1 |
2024-10-14 | *朋 | 10 |
2024-10-22 | *海 | 10 |
2024-10-23 | *南 | 10 |
2024-10-26 | *节 | 6.66 |
2024-10-27 | *o | 5 |
2024-10-28 | W*F | 6.66 |
2024-10-29 | R*n | 6.66 |
2024-11-02 | *球 | 6 |
2024-11-021 | *鑫 | 6.66 |
2024-11-25 | *沙 | 5 |
2024-11-29 | C*n | 2.88 |

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了