前言
只要用过C++的容器,相信大家对迭代器都不会陌生。它提供一种统一的接口形式来遍历相应的容器(例如数组,链表,map等)。
例子1:迭代器的遍历
利用迭代器遍历数组vector
vector<int> vi{ 1, 3, 5, 7, 9 };
for(auto it = vi.begin(); it != vi.end(); ++it) {
cout<<*it<<endl;
}
利用迭代器遍历链表list
list<int> li{ 2, 4, 6, 8, 10 };
for(auto it = li.begin(); it != li.end(); ++it) {
cout<<*it<<endl;
}
大家可以看到,上述两端代码唯一进行修改的就是将vector修改为list(vi修改成li仅仅是为了命名的区别),遍历代码没有任何改变,就可以轻易完成底层存储结构从数组到链表的转化,是不是特别棒?
例子2:一个算法的例子,将当前迭代器向前移动off个单位
算法原型:
advance函数:它接收一个迭代器参数,以及一个偏移量_Off,表示将当前的迭代器向前移动_Off个长度(当_Off<0的时候,相当于向后移动)
template<class _InIt, class _Diff> inline
void advance(_InIt& _Where, _Diff _Off)
{ // increment iterator by offset, arbitrary iterators
_Advance(_Where, _Off, _Iter_cat(_Where));
}
利用advance函数将vector的迭代器it向前移动3个单位
auto it = vi.begin(); //vi = {1, 3, 5, 7, 9}
cout<<*it<<endl; //we get 1
advance(it, 3);
cout<<*it<<endl; //we get 7
利用advance函数将list的迭代器it向前移动3个单位
auto it = li.begin(); //li = {2, 4, 6, 8, 10}
cout<<*it<<endl; //we get 2
advance(it, 3);
cout<<*it<<endl; //we get 8
例子1和2形象的展示了迭代器处理的抽象一致性。不用关心底层容器的结构构建,而用一种统一的方式来对容器进行处理,而且在此基础上进一步实现通用一致的算法,岂不妙哉。当需要修改底层容器的结构时候,只需要修改容器的创建代码,其他算法层面基本不需要改动。
有没有感觉到它的美,那么很容易疑问它是如何实现不同容易的统一调用的呢?在回答这个问题之前,我想先简单说一下Java的实现方案(本人只根据个人想法来写,也许与实际的情况有一点点的偏差,不过本人可以担保,思想肯定是这样的,编程学的就是思想!)
Java的实现
首先盗取网上的一张图(本来是C#的,其实Java实现也是这个结构)
Aggregate类可以认为是集合抽象类,Iterator是迭代器抽象类。迭代器抽象类包含几个抽象方法(可见名知意)。如果需要实现一个具体容器,首先需要实现抽象容器的方法,还需要实现一个具体迭代器类,它通过具体容器的CreateIterator()函数进行绑定。这样ConcreteIterator就持有了具体容器的this指针,就可以实现相对应的first,next等方法了。
例子3:以java中的数组ArrayList为例,代码来源于A 和 B,原谅我未经同意就盗取了代码,只是为了分享知识。
首先实现具体的容器类ArrayList,它继承一个抽象容器接口Collection,大概形如
public class ArrayList implements Collection {
Object[] objects = new Object[10];
int index = 0;
@Override
public void add(Object o) {
if(index == objects.length) {
Object[] newObjects = new Object[objects.length * 2];
System.arraycopy(objects, 0, newObjects, 0, objects.length);
objects = newObjects;
}
objects[index] = o;
index ++;
}
...
}
代码中只简单显示了add函数的实现。ArrayList首先内部创建了一个数据objects,当增加新的元素时,先判断数组是否已经满了,如果已满,那么重新分配一个长度*2的数组,将元素复制过去,然后将新增的元素放入到index指向的位置,并将index增加1。其实index就代表是数组中元素的个数。
然后实现它的迭代器类,ArrayList实现了一个内部类,ArrayListIterator,它继承于Iterator抽象接口,并实现相应的接口方法。示例代码如下:
private class