如何在遍历中使用 iterator/reverse_iterator 删除元素
本文遵循“署名-非商业用途-保持一致”创作公用协议

众所周知,在使用迭代器遍历 STL 容器时,需要特别留意是否在循环中修改了迭代器而导致迭代器失效的情形。下面我来总结一下在对各种容器进行正向和反向遍历过程中删除元素时,正确更新迭代器的用法。本文完整源码:点此查看

首先,要明白使用正向迭代器(iterator)进行反向遍历是错误的用法,要不干嘛要有反向迭代器呢(reverse_iterator)。其次,根据容器的特性,遍历删除操作的用法可以分为两组,第一组是 list 和 vector,第二组是 map 和 set。

接下来,看看具体怎么个用法。

第一种情形:正向遍历删除元素

对 list 和 vector 来说,它们的 erase 函数会返回下一个迭代器,因此在遍历时,只需要 it = c.erase(it); 即可。

对 map 和 set 来说,它们的 erase 函数返回的 void,而在进行 erase 之后,当前迭代器会失效,无法再用于获取下一个迭代器。因此需要 erase 之前就获取指向下一个元素的迭代器。如:

tmpIt = it;
++it;
c.erase(tmpIt);

利用后缀++操作符的特性(先创建副本,然后再递增迭代器,然后返回副本)上面的三行代码可以简化为一行:

c.erase(it++);

下面来看实例:

list 正向遍历删除元素示例(vector 用法相同)

    // erase with iterator
list<int>::iterator it;
for (it = l.begin(); it != l.end();)
{
if ( == (*it) % ) {
it = l.erase(it);
}
else {
++it;
}
}

map 正向遍历删除元素示例(set 用法相同)

    // erase with iterator
map<int, int>::iterator mit;
for (mit = m.begin(); mit != m.end();)
{
if ( == mit->first % ) {
m.erase(mit++);
}
else {
++mit;
}
}

第二种情形,反向遍历删除元素

关于正向/反向迭代器的关系,请参考《Effective STL》,在这里我只说明一点,两者相差一个元素,从一个反向迭代器获得对应的正向迭代器需要使用 base() 方法。如下图所示:ri 是指向元素3的反向迭代器,而 i 是 ri.base() 所得到的正想迭代器。

由于所有的 erase 函数都只接受正向迭代器 iterator,所以在进行反向遍历删除元素时,首先需要将 reverse_iterator 转换为 iterator,然后再考虑更新迭代器的问题。

先来分析如何将 reverse_iterator 转换为 iterator。如上图所示,我们想要删除元素3,而 ri.base() 所得到的正向迭代器 i 指向的其实 4 了,因而为了正确地删除元素 3,需要将ri往前(反向的)挪一个位置。也就是说,这一步的删除用法应为:

c.erase((++rit).base());

或:(想想为什么?,但这个用法不具备可移植性,因为有些 STL 实现不允许修改函数返回的指针)

c.erase(--(rit.base();

然后,我们来分析迭代器更新的问题。
对 list/vector 来说,由于的 erase 能够返回一个有效的正向迭代器,因而只需要将返回的正向迭代器转换为反向迭代器即可。

对 map/set 来说,因为在进行删除操作 l.erase((++rit).base()) 时,迭代器已经更新过了,真是一举两得啊。从这里也可以看出,使用这种先递增后 base() 的转换删除法,代码更清晰。

至此,理论分析完毕,下面我们来看具体的实例。

list 反向遍历删除元素示例(vector 用法相同)

    // erase with reverse_iterator
list<int>::reverse_iterator rit;
for (rit = l.rbegin(); rit != l.rend();)
{
if ( == (*rit) % ) {
rit = list<int>::reverse_iterator(l.erase((++rit).base()));
//rit = list<int>::reverse_iterator(l.erase(--(rit.base()));
}
else {
++rit;
}
}

map 反向遍历删除元素示例(set 用法相同)

    // erase with reverse_iterator
map<int, int>::reverse_iterator rit;
for (rit = m.rbegin(); rit != m.rend();)
{
if ( == rit->first % ) {
m.erase((++rit).base());
}
else {
++rit;
}
}

OK,删除用法相信大家都明白了,但是,但是,引起迭代器失效的操作还有插入操作呀,相信聪明的你一定能够举一反三正确更新迭代器~~

如何在遍历中使用 iterator/reverse_iterator 删除元素的更多相关文章

  1. 在遍历中使用 iterator/reverse_iterator 进行 Erase 的使用方法

    在遍历中使用 iterator/reverse_iterator 进行 Erase 的使用方法 罗朝辉 (http://blog.csdn.net/kesalin/) 本文遵循"署名-非商业 ...

  2. 在Python的列表中利用remove()方法删除元素的教程

    在Python的列表中利用remove()方法删除元素的教程 这篇文章主要介绍了在Python的列表中利用remove()方法删除元素的教程,是Python入门中的基础知识,注意其和pop()方法的区 ...

  3. python中List添加、删除元素的几种方法

    一.python中List添加元素的几种方法 List 是 Python 中常用的数据类型,它一个有序集合,即其中的元素始终保持着初始时的定义的顺序(除非你对它们进行排序或其他修改操作).在Pytho ...

  4. 算法Sedgewick第四版-第1章基础-021一双向链表,在遍历时可修改、删除元素

    package algorithms.ADT; /*************************************************************************** ...

  5. C# 在遍历中修改或者移除元素

    ; i >= ; i--) { var l = imgList[i].Trim(); if (!l.ToLower().Contains(".jpg") && ...

  6. Java中ArrayList的删除元素总结

    Java中循环遍历元素,一般有for循环遍历,foreach循环遍历,iterator遍历. 先定义一个List对象 List<String> list = new ArrayList&l ...

  7. 【原理探究】女朋友问我ArrayList遍历时删除元素的正确姿势是什么?

    简介 我们在项目开发过程中,经常会有需求需要删除ArrayList中的某个元素,而使用不正确的删除方式,就有可能抛出异常.或者在面试中,会遇到面试官询问遍历时如何正常删除元素.所以在本篇文章中,我们会 ...

  8. C#数组删除元素

    一.C#数组删除元素 在C#中,只能在动态数组ArrayList类中对数组执行删除元素的操作.因为动态数组是一个可以改变数组长度和元素个数的数据类型. 示例: using System;using S ...

  9. 遍历List过程中删除元素的正确做法(转)

    遍历List过程中删除元素的正确做法   public class ListRemoveTest {     3 public static void main(String[] args) { 4 ...

随机推荐

  1. 云计算P2V的迁移过程

    总结一下客户在传统物理机向虚拟化环境迁移中的典型场景和常用工具及步骤.

  2. Java Web1

    Java Web应用的核心技术是Java Server Page和Servlet.此外,开发一个完整的Java Web应该涉及一下几种概念及技术. 1.Servlet组件       Servlet响 ...

  3. 【C#】Excel做的数据表、SQLParameter代码生成工具

    转载请注明出处http://www.cnblogs.com/Vulpers/ 做了一个小的代码生成工具,用于新建数据表时能够快速生成一些重复性很高的代码,目前仅支持SqlServer数据库及C#语言, ...

  4. C# 微信v3退款

    1.退款需要退款证书.C#都是用p12的证书,双击证书导入,密码是mch_id(商户号) 2.调用微信退款接口进行退款操作 string resp=string.Empty string cert = ...

  5. Android 时间维护服务 TimeService(针对于特殊定制设备)

    此方法针对于无法自动获取网络时间的特殊设备,正常 Android 设备直接调用 System.currentTimeMillis(); 方法获取当前时间即可. TimeService 集成于 Serv ...

  6. java 线程安全不线程不安全

    经常看到一些类,有的说线程安全,有的说线程不安全,顿时懵逼. 线程安全不安全,主要是在多线程执行的情况下,如果由于线程之间抢占资源而造成程序的bug即为线程不安全,下面就拿arraylist 和Vec ...

  7. Freemarker常用指令使用范例

    我的開發環境 框架:           springmvc+freemarker 開發工具:    springsource-tool-suite-2.9.0 JDK版本:    1.6.0_29 ...

  8. Bootstrap的安装

    在了解了HTML和CSS.JS之后,我咨询了之前做过这一块的学长,得知了Bootstrap的存在. Bootstrap (Web前端CSS框架) Bootstrap,来自 Twitter,是目前很受欢 ...

  9. 学习django之python中os模块的函数

    os.sep可以取代操作系统特定的路径分隔符.windows下为 “\\” os.name字符串指示你正在使用的平台.比如对于Windows,它是'nt',而对于Linux/Unix用户,它是'pos ...

  10. 使用Statement操作数据库

    i import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql ...