编程思想之迭代器

什么是迭代器?

迭代器(Iterator)是按照一定的顺序对一个或多个容器中的元素从前往遍历的一种机制,比如for循环就是一种最简单的迭代器,对一个数组的遍历也是一种的迭代遍历的过程。GOF给出的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器有时也称为枚举器(Enumerator),其结构图如下:

迭代器结构图

迭代器其实就是维护一个当前的指针,这个指针可以指向当前的元素,可以返回当前所指向的元素,可以移到下一个元素的位置,通过这个指针可以遍历容器的所有元素。迭代器一般至少会有以下几种方法:

First(); //将指针移至第一个位置或获得第一个元素

GetCurrent(); //获得当前所指向的元素

MoveNext(); //移至下一个元素

如何使用迭代器

既然迭代器是封装里面的实现细节,对外提供方便访问容器元素的接口,那我们就先从使用的角度认识迭代器,看看在各种语言下迭代器是如何使用的。

C++中的迭代器:

void TestIterator()
{vector<int> vec;         // 定义一容器for(int i = 0; i < 5; i++){vec.push_back(i*2);        //添加元素}//用迭代器访问容器中的每个元素cout << "iterator vector:" << endl;for(vector<int>::iterator itr = vec.begin(); itr != vec.end(); itr ++){cout << *itr << "   ";   //itr是一个指针,指向当前的元素, 所以要解引用获得元素值}cout << endl;map<int, string> student;   //创建一个map,对应学号-姓名的键值对//添加元素student.insert(pair<int, string>(1, "张三"));student.insert(pair<int, string>(3, "王五"));student.insert(pair<int, string>(2, "李四"));//遍历容器中的元素cout << "iterator map:" << endl;for (map<int, string>::iterator itr = student.begin(); itr != student.end(); itr ++){cout << itr->first << "-->" << itr->second << endl;}
}

结果:

iterator vector:

0   2   4   6   8

iterator map:

1-->张三

2-->李四

3-->王五

c++中的容器(如vector、map、list、set等)一般会提供四个迭代器:

iterator:正向迭代,从前往后遍历,可修改元素的值

const_iterator:正向常量迭代,但不能修改元素的值,因为指向的是const的引用

reverse_iterator:反向迭代,从后往前遍历,可修改元素的值

const_reverse_iterator:反向常量迭代,但不能修改元素的值,因为指向的是const的引用

每一种迭代器都提供一对首尾位置的标志begin和end,其关系如下:

迭代器类型

开始位置标志

末尾位置标志

说明

iterator

begin()

end()

正向迭代

const_iterator

cbegin()

cend()

正向常量迭代

reverse_iterator

rbegin()

rend()

反向迭代

const_reverse_iterator

crbegin()

crend()

反向常量迭代

对应的示意图如下:

图1:正常的迭代

图2:常量值的迭代

Java中的迭代器:

public static void testIterator() {//创建一个列表List<Integer> list = new ArrayList<Integer>();list.add(4);   //添加元素list.add(3);list.add(7);//返回一个迭代器,并遍历列表中的元素Iterator<Integer> iterator = list.iterator();while (iterator.hasNext()) {Integer value = iterator.next();System.out.print(value + "    ");}System.out.println();//返回ListIterator迭代器并从后往前遍历列表的元素ListIterator<Integer> listIterator = list.listIterator(list.size());System.out.println("ListIterator:");while (listIterator.hasPrevious()) {Integer value = listIterator.previous();System.out.print(value + "    ");}System.out.println();
}

结果:

Iterator begin to end:

4    3    7

ListIterator end to begin:

7    3    4

Java中的List接口及其实现类可以通过iterator()返回Iterator,或通过listIterator()和listIterator(int index) 返回ListIterator。

Iterator和ListIterator都是迭代器,ListIterator继承自Iterator。Iterator只能对列表进行遍历,且只能从前往后遍历,ListIterator可以修改列表,且可以选择往前或往后遍历。关于Iterator和ListIterator更详细的说明请参见官方API:Iterator和ListIterator

JavaScript中的迭代器

JavaScript中表示容器的是Array类型,标准并没有给出对应的迭代器的说明和实现,但我们可以自己实现一个简单的迭代器的功能:

<script type="text/javascript">//创建一个迭代器,传入的必须是Array类型的数据function makeIterator(array) {var index = 0;return {hasNext: function () {return index < array.length;},next: function () {return this.hasNext ? array[index++] : null;},current: function () {return array[index];}};}//创建一个数组并赋值var mycars=new Array();mycars[0]="Saab";mycars[1]="Volvo";mycars[2]="BMW";//将数组mycars生成一个迭代器,并通过迭代器遍历数据元素var iterator = makeIterator(mycars);while (iterator.hasNext()) {document.write(iterator.next() + '<br>');}
</script>

结果:

Saab

Volvo

BMW

mozilla提供的JavaScript 1.7已经添加了迭代器的功能,但现在只能在Firefox浏览器上才有用。如:

<script type="application/javascript;version=1.7">var lang = { name: 'JavaScript', birthYear: 1995, age: 19 };//生成一个迭代器var itr = Iterator(lang);document.write('key-value:' + '<br>');for(var key in itr){document.write(key + '<br>');}//这个迭代器遍历每一个key值var itr2 = Iterator(lang, false);document.write('key:' + '<br>');for(var key in itr2){document.write(key + '<br>');}//这个迭代器遍历每一个索引和值var arrs = ['JavaScript', 'Python', 'Haskell'];var itr3 = Iterator(arrs, false);document.write('index-value:' + '<br>');for(let [i, value] in itr3){document.write(i + ':' + value + '<br>');}
</script>

结果:

key-value:

name,JavaScript

birthYear,1995

age,19

key:

name,JavaScript

birthYear,1995

age,19

index-value:

0:JavaScript

1:Python

2:Haskell

更多关于JavaScript 1.7的迭代器请参见:Iterators and Generators

迭代器的高级应用

在上面一小节“JavaScript中的迭代器”中,已经对Array数组实现了自己定义的迭代器。以上讲述的迭代器基本都是集合内部的元素具有相同的数据类型,但实际的开发过程中可能会有更复杂的容器结构,假设有如下的需要:

一个公司有多个部门,每个部门有多个人组成,这些人中有开发人员,有测试人员,和与项目相关的其它人员,其结构如下:

现在要遍历这个公司的所有开发人员,遍历这个公司的所有测试人员。

针对这个需求,我们可以创建一个定制化的迭代器来遍历一个公司所有人员,也可以传入员工类型来遍历指定类型的员工,其类的结构图如下:

对应的实现代码如下:

git@code.csdn.net:luoweifu/iteratortest.git

对应的调用代码如下:

#include "stdafx.h"
#include <string>
#include <iostream>
#include "Person.h"
#include "Department.h"
#include "Company.h"
#include "Enumerator.h"int _tmain(int argc, _TCHAR* argv[])
{Company company("Apabi");Department* pDepartMent1 = new Department("开发1部");Department* pDepartMent2 = new Department("开发2部");Department* pDepartMent3 = new Department("内核研发部");company.AddDepartment(pDepartMent1);company.AddDepartment(pDepartMent2);company.AddDepartment(pDepartMent3);int empId = 1;Person* pPerson11 = new Developer(empId++, "Developer11", "C++", "智慧城市");Person* pPerson12 = new Developer(empId++, "Developer12", "Java", "智慧城市");Person* pPerson13 = new Developer(empId++, "Developer13", "Java", "智慧城市");Person* pPerson14 = new Developer(empId++, "Developer14", "JavaScript", "智慧城市");Person* pPerson15 = new Tester(empId++, "Tester15", "LoadRunner");Person* pPerson16 = new Tester(empId++, "Tester16", "黑盒测试");cout << pPerson16->GetPersonType() << endl;pDepartMent1->AddPerson(pPerson11);pDepartMent1->AddPerson(pPerson12);pDepartMent1->AddPerson(pPerson13);pDepartMent1->AddPerson(pPerson14);pDepartMent1->AddPerson(pPerson15);pDepartMent1->AddPerson(pPerson16);Person* pPerson21 = new Developer(empId++, "Developer21", "IOS", "Mobile");Person* pPerson22 = new Developer(empId++, "Developer22", "Android", "Mobile");Person* pPerson23 = new Tester(empId++, "Tester23", "LoadRunner");Person* pPerson24 = new Tester(empId++, "Tester24", "TestIn");pDepartMent2->AddPerson(pPerson21);pDepartMent2->AddPerson(pPerson22);pDepartMent2->AddPerson(pPerson23);pDepartMent2->AddPerson(pPerson24);Person* pPerson31 = new Developer(empId++, "Developer31", "C++", "CEBX内核");Person* pPerson32 = new Developer(empId++, "Developer32", "C++", "CEBX内核");Person* pPerson33 = new Developer(empId++, "Developer33", "C++", "CEBX内核");Person* pPerson34 = new Developer(empId++, "Developer34", "C++", "CEBX内核");Person* pPerson35 = new Tester(empId++, "Tester35", "LoadRunner");pDepartMent3->AddPerson(pPerson31);pDepartMent3->AddPerson(pPerson32);pDepartMent3->AddPerson(pPerson33);pDepartMent3->AddPerson(pPerson34);pDepartMent3->AddPerson(pPerson35);//遍历所有开发者cout << "遍历所有开发者:" << endl;Enumerator* pEnumerator1 = company.GetEnumerator(PERSON_DEVELOPER);while(pEnumerator1->MoveNext()){Person* pPerson = pEnumerator1->Current();if (pPerson){pPerson->showInfo();}}delete pEnumerator1;//遍历所有测试人员cout << "遍历所有测试人员:" << endl;Enumerator* pEnumerator2 = company.GetEnumerator(PERSON_TESTER);while(pEnumerator2->MoveNext()){Person* pPerson = pEnumerator2->Current();if (pPerson){pPerson->showInfo();}}delete pEnumerator2;//遍历公司所有员工cout << "遍历公司所有员工:" << endl;Enumerator* pEnumerator3 = company.GetEnumerator(PERSON_TYPE_NONE);while(pEnumerator3->MoveNext()){Person* pPerson = pEnumerator3->Current();if (pPerson){pPerson->showInfo();}}delete pEnumerator3;return 0;
}

这样就使得代码简洁易懂易读。

迭代器的应用场景

1.集合的内部结构复杂,不想暴露对象的内部细节,只提供精简的访问方式;

2.需要提供统一的访问接口,从而对不同的集合使用同一的算法。

=====================编程思想系列文章回顾=====================

编程思想之递归

编程思想之回调

C++、Java、JavaScript中迭代器的用法相关推荐

  1. javascript 中 console 的用法

    javascript 中 console 的用法 视频 https://www.bilibili.com/video/BV1g7411L751?from=search&seid=1567656 ...

  2. Java程序中Timer的用法

    Java程序中Timer的用法 import java.io.IOException; import java.util.Timer; public class CheckTimer {/*** @p ...

  3. JavaScript中window.open用法实例详解

    本文较为详细的分析了JavaScript中window.open用法.分享给大家供大家参考.具体如下: 复制代码 代码如下: <script LANGUAGE="javascript& ...

  4. javascript 中this 的用法:

    javascript 中this 的用法: 1.<div οnclick="// 可以在里面使用this">division element</div> t ...

  5. java stream中Collectors的用法

    文章目录 简介 Collectors.toList() Collectors.toSet() Collectors.toCollection() Collectors.toMap() Collecto ...

  6. JAVA链表中迭代器的实现

    注:本文代码出自<java数据结构和算法>一书. PS:本文中类的名字定义存在问题,Link9应改为Link.LinkList9应该为LinkList.由于在同包下存在该名称,所以在后面接 ...

  7. 关于JavaScript中typeof的用法

    一.typeof的作用 在JavaScript中,变量未经声明就使用,系统是会报错的.但是,typeof却是js中有且仅有的一个特例. typeof的作用就是用来区分数据类型的,下面先说说typeof ...

  8. javascript中call的用法总结

    javascript中call的用法总结 1.使用call方法调用函数并且指定上下文的'this' function greet(){console.log(this.name+",age= ...

  9. javascript 中innerHTML的用法

    javascript 中innerHTML的用法 语法 Object.innerHTML = "HTML";//设置其内容 var html = Object.innerHTML; ...

最新文章

  1. 云服务干掉的是运维。
  2. CentOS 安装配置memcached
  3. EntityFramework4.5使用Expression类创建动态查询及动态查询导航属性
  4. Python OpenCV人脸识别案例
  5. Java技术:Mybatis-plus常用API全套教程,值得收藏!
  6. 怎么向小学生解释欧拉公式 e^(πi)+1=0?
  7. Python中关于文件路径的简单操作 [转]
  8. Jenkins Ci系列目录
  9. 在NVIDIA Jetson TX2上安装TensorFlow
  10. “得屌丝者得天下”,小米集团回应其高管言论:该员工已请辞...
  11. 数据结构 —— 递归和树
  12. 爬虫实战—模拟登陆oschina
  13. 记一次 “HTTP 405 Method Not Allowed”的解决方法
  14. C++语言程序设计(第4版)郑莉练习
  15. 基金定投--1.基金的基础知识
  16. 加油站的汽油是按什么分类的
  17. 【Android开发】
  18. 北京医院排名(去哪个医院合适)
  19. 几个自用的虚拟机镜像文件,兼容VMware vbox等虚拟机 ova格式直接导入
  20. 如何实现分库分表插件

热门文章

  1. 灰色关联—分析GDP影响因素关联度
  2. 从《秦时明月》聊聊密码学攻防
  3. 一加手机如何拷贝公交卡_谈一谈一加六的公交卡方式
  4. 1.Pytorch3d教程——介绍
  5. ADO.NET三层架构
  6. 小程序通过 ajax读取的图片数据如何展示_一个常被忽略的intouch的小技巧—鼠标悬停...
  7. 从数据下载到极速土地利用变化图制作
  8. Redis秋招面经(自用)
  9. 消防器材RFID固定资产管理系统
  10. python试题出现次数最多的数字_python统计一个数值连续出现的次数